erosolar-cli 1.7.394 → 1.7.396

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.
Files changed (33) hide show
  1. package/dist/core/preferences.d.ts +1 -0
  2. package/dist/core/preferences.d.ts.map +1 -1
  3. package/dist/core/preferences.js +7 -0
  4. package/dist/core/preferences.js.map +1 -1
  5. package/dist/shell/interactiveShell.d.ts +35 -0
  6. package/dist/shell/interactiveShell.d.ts.map +1 -1
  7. package/dist/shell/interactiveShell.js +575 -125
  8. package/dist/shell/interactiveShell.js.map +1 -1
  9. package/dist/shell/shellApp.js +1 -1
  10. package/dist/shell/shellApp.js.map +1 -1
  11. package/dist/shell/terminalInput.d.ts +18 -1
  12. package/dist/shell/terminalInput.d.ts.map +1 -1
  13. package/dist/shell/terminalInput.js +120 -9
  14. package/dist/shell/terminalInput.js.map +1 -1
  15. package/dist/shell/terminalInputAdapter.d.ts +14 -0
  16. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  17. package/dist/shell/terminalInputAdapter.js +14 -0
  18. package/dist/shell/terminalInputAdapter.js.map +1 -1
  19. package/dist/shell/updateManager.d.ts +8 -1
  20. package/dist/shell/updateManager.d.ts.map +1 -1
  21. package/dist/shell/updateManager.js +4 -2
  22. package/dist/shell/updateManager.js.map +1 -1
  23. package/dist/ui/ShellUIAdapter.d.ts +6 -1
  24. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  25. package/dist/ui/ShellUIAdapter.js +26 -5
  26. package/dist/ui/ShellUIAdapter.js.map +1 -1
  27. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  28. package/dist/ui/shortcutsHelp.js +1 -0
  29. package/dist/ui/shortcutsHelp.js.map +1 -1
  30. package/dist/ui/unified/index.d.ts +2 -2
  31. package/dist/ui/unified/index.d.ts.map +1 -1
  32. package/dist/ui/unified/index.js.map +1 -1
  33. package/package.json +1 -1
@@ -1,10 +1,11 @@
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';
4
5
  import { display } from '../ui/display.js';
5
6
  import { theme } from '../ui/theme.js';
6
7
  import { getContextWindowTokens } from '../core/contextWindow.js';
7
- import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
8
+ import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, MissingSecretError, maskSecret, setSecretValue, } from '../core/secretStore.js';
8
9
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
9
10
  import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
10
11
  import { buildEnabledToolSet, evaluateToolPermissions, getToolToggleOptions, } from '../capabilities/toolRegistry.js';
@@ -35,7 +36,6 @@ import { generateTestFlows, detectBugs, detectUIUpdates, saveTestFlows, saveBugR
35
36
  import { TerminalInputAdapter } from './terminalInputAdapter.js';
36
37
  import { renderSessionFrame } from '../ui/unified/layout.js';
37
38
  import { isUpdateInProgress, maybeOfferCliUpdate } from './updateManager.js';
38
- import { writeLock } from '../ui/writeLock.js';
39
39
  import { enterStreamingMode, exitStreamingMode } from '../ui/globalWriteLock.js';
40
40
  import { setGlobalAIEnhancer } from '../tools/localExplore.js';
41
41
  import { createProvider } from '../providers/providerFactory.js';
@@ -119,6 +119,7 @@ export class InteractiveShell {
119
119
  statusSubscription = null;
120
120
  followUpQueue = [];
121
121
  isDrainingQueue = false;
122
+ apiKeyGateActive = false;
122
123
  activeContextWindowTokens = null;
123
124
  latestTokenUsage = { used: null, limit: null };
124
125
  planApprovalBridgeRegistered = false;
@@ -128,6 +129,8 @@ export class InteractiveShell {
128
129
  autosaveEnabled;
129
130
  autoContinueEnabled;
130
131
  verificationEnabled = true;
132
+ alphaZeroModeEnabled;
133
+ alphaZeroVerificationSnapshot = null;
131
134
  editGuardMode = 'display-edits';
132
135
  pendingPermissionInput = null;
133
136
  pendingHistoryLoad = null;
@@ -149,6 +152,13 @@ export class InteractiveShell {
149
152
  lastUserQuery = '';
150
153
  lastFailure = null;
151
154
  lastAutoTestRun = null;
155
+ runIdCounter = 0;
156
+ lastRunLog = null;
157
+ lastReflectedRunId = null;
158
+ skipNextAutoReflection = false;
159
+ alphaZeroAutoImproveActive = false;
160
+ alphaZeroAutoImproveIterations = 0;
161
+ alphaZeroAutoImproveMaxIterations = 6;
152
162
  // Auto-build tracking
153
163
  autoBuildInFlight = false;
154
164
  lastAutoBuildRun = null;
@@ -161,10 +171,12 @@ export class InteractiveShell {
161
171
  lastStreamingElapsedSeconds = null; // Preserve final elapsed time
162
172
  statusLineState = null;
163
173
  statusMessageOverride = null;
174
+ hasShownThoughtProcess = false;
164
175
  promptRefreshTimer = null;
165
176
  launchPaletteShown = false;
166
177
  version;
167
178
  alternateScreenEnabled;
179
+ inputInitialized = false;
168
180
  constructor(config) {
169
181
  this.profile = config.profile;
170
182
  this.profileLabel = config.profileLabel;
@@ -176,6 +188,7 @@ export class InteractiveShell {
176
188
  this.thinkingMode = this.sessionPreferences.thinkingMode;
177
189
  this.autosaveEnabled = this.sessionPreferences.autosave;
178
190
  this.autoContinueEnabled = this.sessionPreferences.autoContinue;
191
+ this.alphaZeroModeEnabled = this.sessionPreferences.alphaZeroMode;
179
192
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
180
193
  this._enabledPlugins = config.enabledPlugins ?? [];
181
194
  this.version = config.version ?? '0.0.0';
@@ -231,6 +244,11 @@ export class InteractiveShell {
231
244
  description: 'AlphaZero offensive security run (start/status/next)',
232
245
  category: 'automation',
233
246
  });
247
+ this.slashCommands.push({
248
+ command: '/alphazero',
249
+ description: 'Toggle AlphaZero RL mode with full-cycle verification',
250
+ category: 'mode',
251
+ });
234
252
  this.statusTracker = config.statusTracker;
235
253
  this.ui = config.ui;
236
254
  this.uiAdapter = config.ui.adapter;
@@ -242,7 +260,11 @@ export class InteractiveShell {
242
260
  });
243
261
  // Set up tool status callback to update status during tool execution
244
262
  this.uiAdapter.setToolStatusCallback((status) => {
245
- this.updateStatusMessage(status ?? null);
263
+ const statusText = status?.text ?? null;
264
+ if (statusText) {
265
+ this.terminalInput.recordRecentAction(`[tool] ${statusText}`);
266
+ }
267
+ this.updateStatusMessage(statusText, { logRecent: false });
246
268
  });
247
269
  this.skillRepository = new SkillRepository({
248
270
  workingDir: this.workingDir,
@@ -262,14 +284,13 @@ export class InteractiveShell {
262
284
  onToggleVerify: () => this.toggleVerificationMode(),
263
285
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
264
286
  onToggleThinking: () => this.cycleThinkingMode(),
287
+ onToggleAlphaZero: () => this.toggleAlphaZeroMode('shortcut'),
265
288
  onClearContext: () => this.handleClearContext(),
266
289
  });
267
290
  // Initialize Alpha Zero 2 metrics tracking
268
291
  this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
269
292
  this.setupStatusTracking();
270
293
  this.refreshContextGauge();
271
- // Start terminal input (sets up handlers)
272
- this.terminalInput.start();
273
294
  // Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
274
295
  this.registerPlanApprovalBridge();
275
296
  // Capture display output into the scrollback/chat log so system messages
@@ -287,9 +308,6 @@ export class InteractiveShell {
287
308
  // Stream banner first - this sets up scroll region dynamically
288
309
  const banner = this.buildBanner();
289
310
  this.terminalInput.streamContent(banner + '\n\n');
290
- // Render chat box after banner is streamed
291
- this.refreshControlBar();
292
- this.terminalInput.forceRender();
293
311
  this.rebuildAgent();
294
312
  this.setupHandlers();
295
313
  this.refreshBannerSessionInfo();
@@ -304,7 +322,13 @@ export class InteractiveShell {
304
322
  this.parallelAgentDisplayLines = manager.formatDisplay();
305
323
  // Trigger UI refresh if streaming
306
324
  if (this.streamingHeartbeatStart) {
307
- this.displayParallelAgents();
325
+ this.uiUpdates.enqueue({
326
+ lane: 'stream',
327
+ mode: ['streaming', 'processing'],
328
+ coalesceKey: 'parallel-agents',
329
+ description: 'parallel agent status',
330
+ run: () => this.displayParallelAgents(),
331
+ });
308
332
  }
309
333
  };
310
334
  manager.on('agent:started', updateDisplay);
@@ -375,17 +399,88 @@ export class InteractiveShell {
375
399
  this.sessionResumeNotice = null;
376
400
  }
377
401
  async start(initialPrompt) {
378
- // Check for updates in background (non-blocking)
379
- if (this.version) {
380
- void maybeOfferCliUpdate(this.version);
381
- }
402
+ await this.runStartupUpdatePrompt();
403
+ this.ensureInputInitialized();
382
404
  if (initialPrompt) {
383
405
  await this.processInputBlock(initialPrompt);
384
406
  return;
385
407
  }
386
408
  this.showLaunchCommandPalette();
387
409
  // Ensure the terminal input is visible
388
- this.terminalInput.render();
410
+ this.renderPromptArea();
411
+ }
412
+ async runStartupUpdatePrompt() {
413
+ if (this.version) {
414
+ try {
415
+ await maybeOfferCliUpdate(this.version, {
416
+ prompt: (context) => this.promptForCliUpdate(context),
417
+ });
418
+ }
419
+ catch {
420
+ // Ignore update check failures at startup
421
+ }
422
+ }
423
+ this.showStartupSecretGuidance();
424
+ }
425
+ ensureInputInitialized() {
426
+ if (this.inputInitialized) {
427
+ return;
428
+ }
429
+ this.terminalInput.start();
430
+ this.refreshControlBar();
431
+ this.renderPromptArea(true);
432
+ this.inputInitialized = true;
433
+ }
434
+ async promptForCliUpdate(context) {
435
+ if (!input.isTTY || !output.isTTY) {
436
+ return true;
437
+ }
438
+ const lines = [
439
+ theme.gradient.primary('⬆️ Update available'),
440
+ `${theme.ui.muted('Current')}: ${theme.info(context.currentVersion)}`,
441
+ `${theme.ui.muted('Latest')}: ${theme.success(context.latestVersion)}`,
442
+ '',
443
+ `${theme.primary('>')} Update now (recommended)`,
444
+ `${theme.ui.muted(' Skip for now')}`,
445
+ '',
446
+ 'Press Enter to accept the highlighted option, or type Y/n (1/2).',
447
+ ];
448
+ display.showSystemMessage(lines.join('\n'));
449
+ const prompt = createInterface({ input, output });
450
+ try {
451
+ const raw = (await prompt.question('> ')).trim().toLowerCase();
452
+ if (!raw || raw === 'y' || raw === 'yes' || raw === '1' || raw === 'update') {
453
+ return true;
454
+ }
455
+ if (raw === 'n' || raw === 'no' || raw === '2' || raw === 'skip') {
456
+ return false;
457
+ }
458
+ return false;
459
+ }
460
+ finally {
461
+ prompt.close();
462
+ }
463
+ }
464
+ showStartupSecretGuidance() {
465
+ const definitions = listSecretDefinitions();
466
+ const providerSecret = definitions.find((definition) => definition.providers.includes(this.sessionState.provider));
467
+ const missingProviderKey = providerSecret ? !getSecretValue(providerSecret.id) : false;
468
+ const hasSearchKey = Boolean(getSecretValue('TAVILY_API_KEY')) ||
469
+ Boolean(getSecretValue('BRAVE_SEARCH_API_KEY')) ||
470
+ Boolean(getSecretValue('SERPAPI_API_KEY'));
471
+ if (!missingProviderKey && hasSearchKey) {
472
+ return;
473
+ }
474
+ const lines = [];
475
+ lines.push(theme.gradient.primary('Quick setup needed:'));
476
+ if (missingProviderKey && providerSecret) {
477
+ lines.push(`${theme.primary('•')} Set ${providerSecret.label} to use ${this.providerLabel(this.sessionState.provider)}.`);
478
+ }
479
+ if (!hasSearchKey) {
480
+ lines.push(`${theme.primary('•')} Add a web search API key (Tavily recommended) for web tools.`);
481
+ }
482
+ lines.push(theme.ui.muted('Run /secrets to configure keys now. Stored values override env vars.'));
483
+ display.showSystemMessage(lines.join('\n'));
389
484
  }
390
485
  showLaunchCommandPalette() {
391
486
  // Disabled: Quick commands palette takes up too much space
@@ -482,6 +577,7 @@ export class InteractiveShell {
482
577
  // Mode toggles
483
578
  '/thinking',
484
579
  '/autocontinue',
580
+ '/alphazero',
485
581
  // Discovery and plugins
486
582
  '/local', '/discover',
487
583
  '/plugins',
@@ -533,7 +629,7 @@ export class InteractiveShell {
533
629
  display.showSystemMessage('✏️ Display edits mode enabled.');
534
630
  }
535
631
  }
536
- this.terminalInput.render();
632
+ this.renderPromptArea();
537
633
  }
538
634
  toggleVerificationMode() {
539
635
  this.setVerificationMode(!this.verificationEnabled, 'shortcut');
@@ -570,6 +666,37 @@ export class InteractiveShell {
570
666
  : 'The model will not be auto-prompted to continue.') +
571
667
  ' Toggle with Ctrl+Shift+C.');
572
668
  }
669
+ toggleAlphaZeroMode(source = 'shortcut') {
670
+ this.setAlphaZeroMode(!this.alphaZeroModeEnabled, source);
671
+ }
672
+ setAlphaZeroMode(enabled, source) {
673
+ const changed = this.alphaZeroModeEnabled !== enabled;
674
+ this.alphaZeroModeEnabled = enabled;
675
+ saveSessionPreferences({ alphaZeroMode: this.alphaZeroModeEnabled });
676
+ // Force verification on while AlphaZero mode is active to guarantee deep checks
677
+ if (enabled) {
678
+ if (this.alphaZeroVerificationSnapshot === null) {
679
+ this.alphaZeroVerificationSnapshot = this.verificationEnabled;
680
+ }
681
+ if (!this.verificationEnabled) {
682
+ this.setVerificationMode(true, 'command');
683
+ }
684
+ }
685
+ else if (this.alphaZeroVerificationSnapshot !== null) {
686
+ this.setVerificationMode(this.alphaZeroVerificationSnapshot, 'command');
687
+ this.alphaZeroVerificationSnapshot = null;
688
+ }
689
+ this.refreshControlBar();
690
+ if (!changed && source === 'shortcut') {
691
+ return;
692
+ }
693
+ if (enabled) {
694
+ display.showSystemMessage('♞ AlphaZero RL mode enabled. Difficult prompts will use the duel/self-critique playbook with full lifecycle verification.');
695
+ }
696
+ else {
697
+ display.showInfo('AlphaZero RL mode disabled. Returning to standard flow.');
698
+ }
699
+ }
573
700
  /**
574
701
  * Cycle through thinking modes (Ctrl+Shift+T keyboard shortcut).
575
702
  */
@@ -647,7 +774,7 @@ export class InteractiveShell {
647
774
  if (['n', 'no', 'cancel', '/cancel'].includes(lower)) {
648
775
  this.pendingPermissionInput = null;
649
776
  display.showInfo('Request cancelled.');
650
- this.terminalInput.render();
777
+ this.renderPromptArea();
651
778
  return null;
652
779
  }
653
780
  // Treat any other input as a replacement request that also needs confirmation
@@ -665,7 +792,7 @@ export class InteractiveShell {
665
792
  ]
666
793
  .filter(Boolean)
667
794
  .join('\n'));
668
- this.terminalInput.render();
795
+ this.renderPromptArea();
669
796
  }
670
797
  /**
671
798
  * Handle Ctrl+C presses in three stages:
@@ -680,7 +807,7 @@ export class InteractiveShell {
680
807
  this.clearChatInput();
681
808
  const prefix = hadBuffer ? 'Input cleared.' : 'Nothing to clear.';
682
809
  display.showSystemMessage(`${prefix} Press Ctrl+C again to pause the AI; a third time quits.`);
683
- this.terminalInput.render();
810
+ this.renderPromptArea();
684
811
  return;
685
812
  }
686
813
  if (this.ctrlCPressCount === 2) {
@@ -781,8 +908,11 @@ export class InteractiveShell {
781
908
  /**
782
909
  * Update status bar message
783
910
  */
784
- updateStatusMessage(message) {
911
+ updateStatusMessage(message, options = {}) {
785
912
  this.statusMessageOverride = message;
913
+ if (message && options.logRecent !== false) {
914
+ this.terminalInput.recordRecentAction(`[status] ${message}`);
915
+ }
786
916
  // During streaming we still want the spinner prefix; when idle force a fast refresh.
787
917
  this.refreshStatusLine(!this.isProcessing);
788
918
  }
@@ -794,26 +924,26 @@ export class InteractiveShell {
794
924
  const trimmed = input.trim();
795
925
  if (!trimmed) {
796
926
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
797
- this.terminalInput.render();
927
+ this.renderPromptArea();
798
928
  return;
799
929
  }
800
930
  const normalized = trimmed.toLowerCase();
801
931
  if (normalized === 'cancel') {
802
932
  this.pendingInteraction = null;
803
933
  display.showInfo('Tool selection cancelled.');
804
- this.terminalInput.render();
934
+ this.renderPromptArea();
805
935
  return;
806
936
  }
807
937
  if (normalized === 'defaults') {
808
938
  pending.selection = buildEnabledToolSet(null);
809
939
  this.renderToolMenu(pending);
810
- this.terminalInput.render();
940
+ this.renderPromptArea();
811
941
  return;
812
942
  }
813
943
  if (normalized === 'save') {
814
944
  await this.persistToolSelection(pending);
815
945
  this.pendingInteraction = null;
816
- this.terminalInput.render();
946
+ this.renderPromptArea();
817
947
  return;
818
948
  }
819
949
  const choice = Number.parseInt(trimmed, 10);
@@ -831,11 +961,11 @@ export class InteractiveShell {
831
961
  }
832
962
  this.renderToolMenu(pending);
833
963
  }
834
- this.terminalInput.render();
964
+ this.renderPromptArea();
835
965
  return;
836
966
  }
837
967
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
838
- this.terminalInput.render();
968
+ this.renderPromptArea();
839
969
  }
840
970
  async persistToolSelection(interaction) {
841
971
  if (setsEqual(interaction.selection, interaction.initialSelection)) {
@@ -862,36 +992,36 @@ export class InteractiveShell {
862
992
  if (!this.agentMenu) {
863
993
  this.pendingInteraction = null;
864
994
  display.showWarning('Agent selection is unavailable in this CLI.');
865
- this.terminalInput.render();
995
+ this.renderPromptArea();
866
996
  return;
867
997
  }
868
998
  const trimmed = input.trim();
869
999
  if (!trimmed) {
870
1000
  display.showWarning('Enter a number or type "cancel".');
871
- this.terminalInput.render();
1001
+ this.renderPromptArea();
872
1002
  return;
873
1003
  }
874
1004
  if (trimmed.toLowerCase() === 'cancel') {
875
1005
  this.pendingInteraction = null;
876
1006
  display.showInfo('Agent selection cancelled.');
877
- this.terminalInput.render();
1007
+ this.renderPromptArea();
878
1008
  return;
879
1009
  }
880
1010
  const choice = Number.parseInt(trimmed, 10);
881
1011
  if (!Number.isFinite(choice)) {
882
1012
  display.showWarning('Please enter a valid number.');
883
- this.terminalInput.render();
1013
+ this.renderPromptArea();
884
1014
  return;
885
1015
  }
886
1016
  const option = pending.options[choice - 1];
887
1017
  if (!option) {
888
1018
  display.showWarning('That option is not available.');
889
- this.terminalInput.render();
1019
+ this.renderPromptArea();
890
1020
  return;
891
1021
  }
892
1022
  await this.persistAgentSelection(option.name);
893
1023
  this.pendingInteraction = null;
894
- this.terminalInput.render();
1024
+ this.renderPromptArea();
895
1025
  }
896
1026
  async persistAgentSelection(profileName) {
897
1027
  if (!this.agentMenu) {
@@ -964,7 +1094,7 @@ export class InteractiveShell {
964
1094
  lines.push(` ${theme.primary('[text]')} Submit your own solution instead`);
965
1095
  lines.push('');
966
1096
  display.showSystemMessage(lines.join('\n'));
967
- this.terminalInput.render();
1097
+ this.renderPromptArea();
968
1098
  }
969
1099
  async handlePlanApprovalInput(input) {
970
1100
  const pending = this.pendingInteraction;
@@ -973,7 +1103,7 @@ export class InteractiveShell {
973
1103
  const trimmed = input.trim();
974
1104
  if (!trimmed) {
975
1105
  display.showWarning('Enter a command or your own solution.');
976
- this.terminalInput.render();
1106
+ this.renderPromptArea();
977
1107
  return;
978
1108
  }
979
1109
  const lower = trimmed.toLowerCase();
@@ -981,7 +1111,7 @@ export class InteractiveShell {
981
1111
  if (lower === 'cancel' || lower === 'c') {
982
1112
  this.pendingInteraction = null;
983
1113
  display.showInfo('Plan cancelled. You can continue with a different approach.');
984
- this.terminalInput.render();
1114
+ this.renderPromptArea();
985
1115
  return;
986
1116
  }
987
1117
  // Select all
@@ -1001,7 +1131,7 @@ export class InteractiveShell {
1001
1131
  const selectedSteps = pending.steps.filter(s => pending.selectedSteps.has(s.id));
1002
1132
  if (selectedSteps.length === 0) {
1003
1133
  display.showWarning('No steps selected. Select steps or enter your own solution.');
1004
- this.terminalInput.render();
1134
+ this.renderPromptArea();
1005
1135
  return;
1006
1136
  }
1007
1137
  this.pendingInteraction = null;
@@ -1034,15 +1164,18 @@ export class InteractiveShell {
1034
1164
  return;
1035
1165
  }
1036
1166
  display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
1037
- this.terminalInput.render();
1167
+ this.renderPromptArea();
1038
1168
  }
1039
1169
  setupHandlers() {
1040
1170
  // Handle terminal resize
1041
1171
  output.on('resize', () => {
1172
+ if (!this.inputInitialized) {
1173
+ return;
1174
+ }
1175
+ this.terminalInput.resetContentPosition();
1042
1176
  this.terminalInput.handleResize();
1177
+ this.terminalInput.forceRender();
1043
1178
  });
1044
- // Show initial input UI
1045
- this.terminalInput.render();
1046
1179
  }
1047
1180
  /**
1048
1181
  * Set up command autocomplete with all available slash commands.
@@ -1073,6 +1206,14 @@ export class InteractiveShell {
1073
1206
  catch {
1074
1207
  // Custom commands are optional
1075
1208
  }
1209
+ // Add manual commands that are not yet in the schema
1210
+ if (!commands.some((cmd) => cmd.command === '/alphazero')) {
1211
+ commands.push({
1212
+ command: '/alphazero',
1213
+ description: 'Toggle AlphaZero RL mode',
1214
+ category: 'mode',
1215
+ });
1216
+ }
1076
1217
  // Sort commands alphabetically
1077
1218
  commands.sort((a, b) => a.command.localeCompare(b.command));
1078
1219
  this.terminalInput.setAvailableCommands(commands);
@@ -1153,23 +1294,51 @@ export class InteractiveShell {
1153
1294
  autoContinueHotkey: 'ctrl+shift+c',
1154
1295
  thinkingModeLabel: this.thinkingMode,
1155
1296
  thinkingHotkey: 'ctrl+shift+t',
1297
+ alphaZeroEnabled: this.alphaZeroModeEnabled,
1298
+ alphaZeroHotkey: 'ctrl+shift+a',
1299
+ alphaZeroLabel: 'AlphaZero RL',
1156
1300
  });
1157
1301
  this.refreshStatusLine();
1158
- this.terminalInput.render();
1302
+ this.renderPromptArea();
1159
1303
  }
1160
1304
  writeLocked(content) {
1161
1305
  if (!content) {
1162
1306
  return;
1163
1307
  }
1164
- // If lock is already held, write directly - we're in a protected context
1165
- // This prevents queuing issues where content gets delayed
1166
- if (writeLock.isLocked()) {
1167
- process.stdout.write(content);
1308
+ // Route through display stream so scroll regions and streaming locks stay in sync
1309
+ display.stream(content);
1310
+ }
1311
+ isStreamingUiActive() {
1312
+ return this.streamingHeartbeatStart !== null;
1313
+ }
1314
+ /**
1315
+ * Render the prompt/control bar. During streaming, rely on the streaming frame
1316
+ * renderer and enqueue through the UIUpdateCoordinator to avoid fighting the
1317
+ * scroll region or duplicating the prompt.
1318
+ */
1319
+ renderPromptArea(force = false) {
1320
+ if (this.isStreamingUiActive()) {
1321
+ this.uiUpdates.enqueue({
1322
+ lane: 'prompt',
1323
+ mode: ['streaming', 'processing'],
1324
+ coalesceKey: 'prompt:streaming-frame',
1325
+ description: 'render streaming prompt frame',
1326
+ run: () => {
1327
+ if (force) {
1328
+ this.terminalInput.renderStreamingFrame(true);
1329
+ return;
1330
+ }
1331
+ this.terminalInput.renderStreamingFrame();
1332
+ },
1333
+ });
1168
1334
  return;
1169
1335
  }
1170
- writeLock.withLock(() => {
1171
- process.stdout.write(content);
1172
- }, 'interactiveShell.stdout');
1336
+ if (force) {
1337
+ this.terminalInput.forceRender();
1338
+ }
1339
+ else {
1340
+ this.terminalInput.render();
1341
+ }
1173
1342
  }
1174
1343
  /**
1175
1344
  * Refresh the status line in the persistent input area.
@@ -1215,7 +1384,7 @@ export class InteractiveShell {
1215
1384
  provider: this.providerLabel(this.sessionState.provider),
1216
1385
  });
1217
1386
  if (forceRender) {
1218
- this.terminalInput.render();
1387
+ this.renderPromptArea(true);
1219
1388
  }
1220
1389
  }
1221
1390
  /**
@@ -1252,7 +1421,7 @@ export class InteractiveShell {
1252
1421
  * Ensure the terminal input is ready for interactive input.
1253
1422
  */
1254
1423
  ensureReadlineReady() {
1255
- this.terminalInput.render();
1424
+ this.renderPromptArea();
1256
1425
  }
1257
1426
  /**
1258
1427
  * Log user prompt to the scroll region so it's part of the conversation flow.
@@ -1274,7 +1443,7 @@ export class InteractiveShell {
1274
1443
  }
1275
1444
  requestPromptRefresh(force = false) {
1276
1445
  if (force) {
1277
- this.terminalInput.forceRender();
1446
+ this.renderPromptArea(true);
1278
1447
  return;
1279
1448
  }
1280
1449
  if (this.promptRefreshTimer) {
@@ -1282,7 +1451,7 @@ export class InteractiveShell {
1282
1451
  }
1283
1452
  this.promptRefreshTimer = setTimeout(() => {
1284
1453
  this.promptRefreshTimer = null;
1285
- this.terminalInput.render();
1454
+ this.renderPromptArea();
1286
1455
  }, 48);
1287
1456
  }
1288
1457
  clearPromptRefreshTimer() {
@@ -1291,6 +1460,25 @@ export class InteractiveShell {
1291
1460
  this.promptRefreshTimer = null;
1292
1461
  }
1293
1462
  }
1463
+ async withStreamingUi(label, run) {
1464
+ if (this.isStreamingUiActive()) {
1465
+ return run();
1466
+ }
1467
+ this.terminalInput.setStreaming(true);
1468
+ this.startStreamingHeartbeat(label);
1469
+ try {
1470
+ return await run();
1471
+ }
1472
+ finally {
1473
+ this.stopStreamingHeartbeat();
1474
+ this.terminalInput.setStreaming(false);
1475
+ const nextMode = this.isProcessing ? 'processing' : 'idle';
1476
+ this.uiUpdates.setMode(nextMode);
1477
+ if (nextMode === 'processing') {
1478
+ queueMicrotask(() => this.uiUpdates.setMode('idle'));
1479
+ }
1480
+ }
1481
+ }
1294
1482
  startStreamingHeartbeat(label = 'Streaming') {
1295
1483
  this.stopStreamingHeartbeat();
1296
1484
  // Enter global streaming mode - blocks all non-streaming UI output
@@ -1371,7 +1559,7 @@ export class InteractiveShell {
1371
1559
  else {
1372
1560
  this.setIdleStatus();
1373
1561
  }
1374
- this.terminalInput.render();
1562
+ this.renderPromptArea();
1375
1563
  }
1376
1564
  enqueueFollowUpAction(action) {
1377
1565
  this.followUpQueue.push(action);
@@ -1390,14 +1578,21 @@ export class InteractiveShell {
1390
1578
  this.refreshQueueIndicators();
1391
1579
  this.scheduleQueueProcessing();
1392
1580
  // Re-show the prompt so user can continue typing more follow-ups
1393
- this.terminalInput.render();
1581
+ this.renderPromptArea();
1394
1582
  }
1395
1583
  scheduleQueueProcessing() {
1396
1584
  if (!this.followUpQueue.length) {
1397
1585
  this.refreshQueueIndicators();
1398
1586
  return;
1399
1587
  }
1588
+ if (this.apiKeyGateActive) {
1589
+ this.refreshQueueIndicators();
1590
+ return;
1591
+ }
1400
1592
  queueMicrotask(() => {
1593
+ if (this.apiKeyGateActive) {
1594
+ return;
1595
+ }
1401
1596
  void this.processQueuedActions();
1402
1597
  });
1403
1598
  }
@@ -1405,12 +1600,12 @@ export class InteractiveShell {
1405
1600
  * Process queued follow-up actions.
1406
1601
  */
1407
1602
  async processQueuedActions() {
1408
- if (this.isDrainingQueue || this.isProcessing || !this.followUpQueue.length) {
1603
+ if (this.apiKeyGateActive || this.isDrainingQueue || this.isProcessing || !this.followUpQueue.length) {
1409
1604
  return;
1410
1605
  }
1411
1606
  this.isDrainingQueue = true;
1412
1607
  try {
1413
- while (!this.isProcessing && this.followUpQueue.length) {
1608
+ while (!this.isProcessing && !this.apiKeyGateActive && this.followUpQueue.length) {
1414
1609
  const next = this.followUpQueue.shift();
1415
1610
  const remaining = this.followUpQueue.length;
1416
1611
  const label = next.type === 'continuous' ? 'continuous command' : 'follow-up';
@@ -1445,12 +1640,12 @@ export class InteractiveShell {
1445
1640
  }
1446
1641
  if (lower === 'clear') {
1447
1642
  display.clear();
1448
- this.terminalInput.render();
1643
+ this.renderPromptArea();
1449
1644
  return;
1450
1645
  }
1451
1646
  if (lower === 'help') {
1452
1647
  this.showHelp();
1453
- this.terminalInput.render();
1648
+ this.renderPromptArea();
1454
1649
  return;
1455
1650
  }
1456
1651
  if (trimmed.startsWith('/')) {
@@ -1460,12 +1655,12 @@ export class InteractiveShell {
1460
1655
  // Check for continuous/infinite loop commands
1461
1656
  if (this.isContinuousCommand(trimmed)) {
1462
1657
  await this.processContinuousRequest(trimmed);
1463
- this.terminalInput.render();
1658
+ this.renderPromptArea();
1464
1659
  return;
1465
1660
  }
1466
1661
  // Direct execution for all inputs, including multi-line pastes
1467
1662
  await this.processRequest(trimmed);
1468
- this.terminalInput.render();
1663
+ this.renderPromptArea();
1469
1664
  }
1470
1665
  /**
1471
1666
  * Check if the command is a continuous/infinite loop command
@@ -1503,6 +1698,153 @@ export class InteractiveShell {
1503
1698
  ];
1504
1699
  return patterns.some(pattern => pattern.test(lower));
1505
1700
  }
1701
+ isDifficultProblem(input) {
1702
+ const normalized = input.toLowerCase();
1703
+ const wordCount = normalized.split(/\s+/).filter(Boolean).length;
1704
+ if (normalized.length > 600 || wordCount > 80) {
1705
+ return true;
1706
+ }
1707
+ const signals = [
1708
+ 'root cause',
1709
+ 'postmortem',
1710
+ 'crash',
1711
+ 'incident',
1712
+ 'outage',
1713
+ 'optimiz',
1714
+ 'performance',
1715
+ 'throughput',
1716
+ 'latency',
1717
+ 'scalab',
1718
+ 'architecture',
1719
+ 'rewrite',
1720
+ 'migration',
1721
+ 'refactor',
1722
+ 'reverse engineer',
1723
+ 'security',
1724
+ 'exploit',
1725
+ 'injection',
1726
+ 'vulnerability',
1727
+ 'compliance',
1728
+ 'multi-step',
1729
+ 'complex',
1730
+ 'difficult',
1731
+ 'hard problem',
1732
+ 'debug',
1733
+ 'trace',
1734
+ 'profil',
1735
+ 'bottleneck',
1736
+ ];
1737
+ return signals.some((signal) => normalized.includes(signal));
1738
+ }
1739
+ buildAlphaZeroPrompt(request, flaggedDifficult) {
1740
+ const playbook = [
1741
+ 'AlphaZero RL MODE is ACTIVE. Operate as a self-play reinforcement loop.',
1742
+ flaggedDifficult
1743
+ ? 'Treat this as a difficult, high-risk task and over-verify the result.'
1744
+ : 'Apply the reinforcement loop even if the task looks small.',
1745
+ 'Follow this closed-loop playbook:',
1746
+ '- Draft two competing solution strategies and merge the strongest ideas before executing.',
1747
+ '- Execute with tools while logging decisions and evidence.',
1748
+ '- Self-critique and repair until the quality is excellent (aim ≥90/100).',
1749
+ '- Run full-lifecycle verification like a human reviewer: build/tests, manual sanity checks, edge cases, performance/safety/security probes, docs/UX/readiness notes.',
1750
+ '- Keep a verification ledger: each check with PASS/FAIL, evidence, and remaining risks. If anything fails, fix and re-verify before claiming completion.',
1751
+ 'Finish with a concise sign-off that lists what was achieved and the proof of completion.',
1752
+ ];
1753
+ return `${playbook.join('\n')}\n\nPrimary user request:\n${request.trim()}`;
1754
+ }
1755
+ buildRunLogExcerpt(startIndex) {
1756
+ const buffer = this.terminalInput.getScrollbackBuffer();
1757
+ const sinceStart = buffer.slice(Math.max(0, startIndex));
1758
+ const excerpt = sinceStart.slice(-200); // Cap prompt size
1759
+ return excerpt.join('\n').trim();
1760
+ }
1761
+ buildRunLogEntry(request, response, scrollbackStartIndex, meta) {
1762
+ return {
1763
+ id: ++this.runIdCounter,
1764
+ request,
1765
+ response,
1766
+ timestamp: new Date().toISOString(),
1767
+ alphaZero: meta.alphaZeroEngaged,
1768
+ difficult: meta.alphaZeroDifficult,
1769
+ failureType: meta.failureType,
1770
+ outputExcerpt: this.buildRunLogExcerpt(scrollbackStartIndex),
1771
+ };
1772
+ }
1773
+ buildAlphaZeroReflectionPrompt(runLog, options) {
1774
+ const lines = [];
1775
+ lines.push('AlphaZero Post-Run Self-Reflection (erosolar-cli)');
1776
+ lines.push(`Timestamp: ${runLog.timestamp}`);
1777
+ lines.push(`AlphaZero: ${runLog.alphaZero ? 'on' : 'off'}${runLog.difficult ? ' | difficult' : ''}${runLog.failureType ? ` | signal: ${runLog.failureType}` : ''}`);
1778
+ lines.push('');
1779
+ lines.push('User request:');
1780
+ lines.push(runLog.request);
1781
+ lines.push('');
1782
+ lines.push('Previous run log excerpt:');
1783
+ lines.push(runLog.outputExcerpt || '[empty]');
1784
+ lines.push('');
1785
+ lines.push('Instructions:');
1786
+ lines.push('- Reflect on the log to spot erosolar-cli bugs, UX issues, or reliability gaps.');
1787
+ lines.push('- Propose and apply targeted fixes in this repository only (no user workspace edits).');
1788
+ lines.push('- Prefer small, test-backed changes; run any relevant checks you invoke.');
1789
+ lines.push('- Keep notes concise and finish with applied changes plus follow-ups.');
1790
+ if (options.autoChain) {
1791
+ lines.push('- Continue iterating automatically while meaningful improvements remain.');
1792
+ lines.push('- When no further improvements are possible, reply with NO_MORE_IMPROVEMENTS on its own line.');
1793
+ }
1794
+ return lines.join('\n');
1795
+ }
1796
+ maybeQueueAlphaZeroSelfReflection(runLog, alphaZeroEngaged, allowAutoChain) {
1797
+ if (!alphaZeroEngaged) {
1798
+ return;
1799
+ }
1800
+ if (!isErosolarRepo(this.workingDir)) {
1801
+ return;
1802
+ }
1803
+ if (!runLog.outputExcerpt) {
1804
+ return;
1805
+ }
1806
+ if (this.lastReflectedRunId === runLog.id) {
1807
+ return;
1808
+ }
1809
+ if (allowAutoChain) {
1810
+ if (!this.autoContinueEnabled) {
1811
+ return;
1812
+ }
1813
+ if (this.alphaZeroAutoImproveIterations >= this.alphaZeroAutoImproveMaxIterations) {
1814
+ display.showInfo('AlphaZero auto-improvement limit reached; stopping.');
1815
+ this.alphaZeroAutoImproveActive = false;
1816
+ this.alphaZeroAutoImproveIterations = 0;
1817
+ return;
1818
+ }
1819
+ if (!this.alphaZeroAutoImproveActive) {
1820
+ this.alphaZeroAutoImproveIterations = 0;
1821
+ }
1822
+ this.alphaZeroAutoImproveActive = true;
1823
+ this.alphaZeroAutoImproveIterations++;
1824
+ }
1825
+ const prompt = this.buildAlphaZeroReflectionPrompt(runLog, { autoChain: allowAutoChain });
1826
+ this.lastReflectedRunId = runLog.id;
1827
+ this.skipNextAutoReflection = !allowAutoChain; // Prevent reflection-on-reflection unless auto-chaining
1828
+ this.enqueueFollowUpAction({ type: 'request', text: prompt });
1829
+ display.showInfo(allowAutoChain
1830
+ ? 'Auto AlphaZero self-improvement queued (auto-continue enabled).'
1831
+ : 'Queued AlphaZero self-reflection to improve erosolar-cli from the latest run log.');
1832
+ }
1833
+ shouldStopAlphaZeroAutoImprove(responseText, allowAutoChain) {
1834
+ if (!this.alphaZeroAutoImproveActive) {
1835
+ return false;
1836
+ }
1837
+ if (!allowAutoChain) {
1838
+ return true;
1839
+ }
1840
+ if (!responseText) {
1841
+ return false;
1842
+ }
1843
+ const normalized = responseText.toLowerCase();
1844
+ return (normalized.includes('no_more_improvements') ||
1845
+ normalized.includes('no more improvements') ||
1846
+ normalized.includes('stop_auto_improve'));
1847
+ }
1506
1848
  async handlePendingInteraction(input) {
1507
1849
  if (!this.pendingInteraction) {
1508
1850
  return false;
@@ -1510,7 +1852,7 @@ export class InteractiveShell {
1510
1852
  switch (this.pendingInteraction.type) {
1511
1853
  case 'model-loading':
1512
1854
  display.showInfo('Still fetching model options. Please wait a moment.');
1513
- this.terminalInput.render();
1855
+ this.renderPromptArea();
1514
1856
  return true;
1515
1857
  case 'model-provider':
1516
1858
  await this.handleModelProviderSelection(input);
@@ -1541,7 +1883,7 @@ export class InteractiveShell {
1541
1883
  const [command] = input.split(/\s+/);
1542
1884
  if (!command) {
1543
1885
  display.showWarning('Enter a slash command.');
1544
- this.terminalInput.render();
1886
+ this.renderPromptArea();
1545
1887
  return;
1546
1888
  }
1547
1889
  switch (command) {
@@ -1599,6 +1941,9 @@ export class InteractiveShell {
1599
1941
  case '/autocontinue':
1600
1942
  this.handleAutoContinueCommand(input);
1601
1943
  break;
1944
+ case '/alphazero':
1945
+ this.handleAlphaZeroCommand(input);
1946
+ break;
1602
1947
  case '/shortcuts':
1603
1948
  case '/keys':
1604
1949
  this.handleShortcutsCommand();
@@ -1703,7 +2048,7 @@ export class InteractiveShell {
1703
2048
  }
1704
2049
  break;
1705
2050
  }
1706
- this.terminalInput.render();
2051
+ this.renderPromptArea();
1707
2052
  }
1708
2053
  async tryCustomSlashCommand(command, fullInput) {
1709
2054
  const custom = this.customCommandMap.get(command);
@@ -1740,6 +2085,7 @@ export class InteractiveShell {
1740
2085
  ` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
1741
2086
  ` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
1742
2087
  ` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
2088
+ ` ${theme.info('Option+A')} ${theme.ui.muted('Toggle AlphaZero RL mode')}`,
1743
2089
  ` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
1744
2090
  ` ${theme.info('Option+X')} ${theme.ui.muted('Clear/compact context')}`,
1745
2091
  '',
@@ -2025,6 +2371,24 @@ export class InteractiveShell {
2025
2371
  };
2026
2372
  display.showInfo(`Thinking mode set to ${theme.info(value)} – ${descriptions[this.thinkingMode]}`);
2027
2373
  }
2374
+ handleAlphaZeroCommand(input) {
2375
+ const value = input.slice('/alphazero'.length).trim().toLowerCase();
2376
+ if (!value || value === 'status') {
2377
+ const status = this.alphaZeroModeEnabled ? theme.success('on') : theme.ui.muted('off');
2378
+ const verification = this.verificationEnabled ? 'verification locked on' : 'verification optional';
2379
+ display.showInfo(`AlphaZero RL mode is ${status}. When enabled, difficult prompts use duel/self-critique and human-style verification (${verification}).`);
2380
+ return;
2381
+ }
2382
+ if (['on', 'enable', 'enabled'].includes(value)) {
2383
+ this.setAlphaZeroMode(true, 'command');
2384
+ return;
2385
+ }
2386
+ if (['off', 'disable', 'disabled'].includes(value)) {
2387
+ this.setAlphaZeroMode(false, 'command');
2388
+ return;
2389
+ }
2390
+ display.showWarning('Usage: /alphazero [on|off|status]');
2391
+ }
2028
2392
  handleShortcutsCommand() {
2029
2393
  // Display keyboard shortcuts help (Claude Code style)
2030
2394
  display.showSystemMessage(formatShortcutsHelp());
@@ -3494,7 +3858,7 @@ export class InteractiveShell {
3494
3858
  display.clear();
3495
3859
  clearAutosaveSnapshot(this.profile);
3496
3860
  display.showInfo('Conversation cleared. Starting fresh.');
3497
- this.terminalInput.render();
3861
+ this.renderPromptArea();
3498
3862
  }
3499
3863
  async handleResumeCommand(input) {
3500
3864
  const tokens = input.split(/\s+/).slice(1);
@@ -3803,7 +4167,7 @@ export class InteractiveShell {
3803
4167
  if (!providerOptions.length) {
3804
4168
  display.showWarning('No providers are available.');
3805
4169
  this.pendingInteraction = null;
3806
- this.terminalInput.render();
4170
+ this.renderPromptArea();
3807
4171
  return;
3808
4172
  }
3809
4173
  const lines = [
@@ -3824,7 +4188,7 @@ export class InteractiveShell {
3824
4188
  catch (error) {
3825
4189
  display.showError('Failed to load model list. Try again in a moment.', error);
3826
4190
  this.pendingInteraction = null;
3827
- this.terminalInput.render();
4191
+ this.renderPromptArea();
3828
4192
  }
3829
4193
  }
3830
4194
  buildProviderOptions() {
@@ -4369,29 +4733,29 @@ export class InteractiveShell {
4369
4733
  const trimmed = input.trim();
4370
4734
  if (!trimmed) {
4371
4735
  display.showWarning('Enter a number or type cancel.');
4372
- this.terminalInput.render();
4736
+ this.renderPromptArea();
4373
4737
  return;
4374
4738
  }
4375
4739
  if (trimmed.toLowerCase() === 'cancel') {
4376
4740
  this.pendingInteraction = null;
4377
4741
  display.showInfo('Model selection cancelled.');
4378
- this.terminalInput.render();
4742
+ this.renderPromptArea();
4379
4743
  return;
4380
4744
  }
4381
4745
  const choice = Number.parseInt(trimmed, 10);
4382
4746
  if (!Number.isFinite(choice)) {
4383
4747
  display.showWarning('Please enter a valid number.');
4384
- this.terminalInput.render();
4748
+ this.renderPromptArea();
4385
4749
  return;
4386
4750
  }
4387
4751
  const option = pending.options[choice - 1];
4388
4752
  if (!option) {
4389
4753
  display.showWarning('That option is not available.');
4390
- this.terminalInput.render();
4754
+ this.renderPromptArea();
4391
4755
  return;
4392
4756
  }
4393
4757
  this.showProviderModels(option);
4394
- this.terminalInput.render();
4758
+ this.renderPromptArea();
4395
4759
  }
4396
4760
  async handleModelSelection(input) {
4397
4761
  const pending = this.pendingInteraction;
@@ -4401,35 +4765,35 @@ export class InteractiveShell {
4401
4765
  const trimmed = input.trim();
4402
4766
  if (!trimmed) {
4403
4767
  display.showWarning('Enter a number, type "back", or type "cancel".');
4404
- this.terminalInput.render();
4768
+ this.renderPromptArea();
4405
4769
  return;
4406
4770
  }
4407
4771
  if (trimmed.toLowerCase() === 'back') {
4408
4772
  this.showModelMenu();
4409
- this.terminalInput.render();
4773
+ this.renderPromptArea();
4410
4774
  return;
4411
4775
  }
4412
4776
  if (trimmed.toLowerCase() === 'cancel') {
4413
4777
  this.pendingInteraction = null;
4414
4778
  display.showInfo('Model selection cancelled.');
4415
- this.terminalInput.render();
4779
+ this.renderPromptArea();
4416
4780
  return;
4417
4781
  }
4418
4782
  const choice = Number.parseInt(trimmed, 10);
4419
4783
  if (!Number.isFinite(choice)) {
4420
4784
  display.showWarning('Please enter a valid number.');
4421
- this.terminalInput.render();
4785
+ this.renderPromptArea();
4422
4786
  return;
4423
4787
  }
4424
4788
  const preset = pending.options[choice - 1];
4425
4789
  if (!preset) {
4426
4790
  display.showWarning('That option is not available.');
4427
- this.terminalInput.render();
4791
+ this.renderPromptArea();
4428
4792
  return;
4429
4793
  }
4430
4794
  this.pendingInteraction = null;
4431
4795
  await this.applyModelPreset(preset);
4432
- this.terminalInput.render();
4796
+ this.renderPromptArea();
4433
4797
  }
4434
4798
  async applyModelPreset(preset) {
4435
4799
  try {
@@ -4462,30 +4826,30 @@ export class InteractiveShell {
4462
4826
  const trimmed = input.trim();
4463
4827
  if (!trimmed) {
4464
4828
  display.showWarning('Enter a number or type cancel.');
4465
- this.terminalInput.render();
4829
+ this.renderPromptArea();
4466
4830
  return;
4467
4831
  }
4468
4832
  if (trimmed.toLowerCase() === 'cancel') {
4469
4833
  this.pendingInteraction = null;
4470
4834
  display.showInfo('Secret management cancelled.');
4471
- this.terminalInput.render();
4835
+ this.renderPromptArea();
4472
4836
  return;
4473
4837
  }
4474
4838
  const choice = Number.parseInt(trimmed, 10);
4475
4839
  if (!Number.isFinite(choice)) {
4476
4840
  display.showWarning('Please enter a valid number.');
4477
- this.terminalInput.render();
4841
+ this.renderPromptArea();
4478
4842
  return;
4479
4843
  }
4480
4844
  const secret = pending.options[choice - 1];
4481
4845
  if (!secret) {
4482
4846
  display.showWarning('That option is not available.');
4483
- this.terminalInput.render();
4847
+ this.renderPromptArea();
4484
4848
  return;
4485
4849
  }
4486
4850
  display.showSystemMessage(`Enter a new value for ${secret.label} or type "cancel".`);
4487
4851
  this.pendingInteraction = { type: 'secret-input', secret };
4488
- this.terminalInput.render();
4852
+ this.renderPromptArea();
4489
4853
  }
4490
4854
  async handleSecretInput(input) {
4491
4855
  const pending = this.pendingInteraction;
@@ -4495,14 +4859,16 @@ export class InteractiveShell {
4495
4859
  const trimmed = input.trim();
4496
4860
  if (!trimmed) {
4497
4861
  display.showWarning('Enter a value or type cancel.');
4498
- this.terminalInput.render();
4862
+ this.renderPromptArea();
4499
4863
  return;
4500
4864
  }
4501
4865
  if (trimmed.toLowerCase() === 'cancel') {
4502
4866
  this.pendingInteraction = null;
4503
4867
  this.pendingSecretRetry = null;
4868
+ this.apiKeyGateActive = false;
4504
4869
  display.showInfo('Secret unchanged.');
4505
- this.terminalInput.render();
4870
+ this.renderPromptArea();
4871
+ this.scheduleQueueProcessing();
4506
4872
  return;
4507
4873
  }
4508
4874
  try {
@@ -4511,6 +4877,7 @@ export class InteractiveShell {
4511
4877
  this.pendingInteraction = null;
4512
4878
  const deferred = this.pendingSecretRetry;
4513
4879
  this.pendingSecretRetry = null;
4880
+ this.apiKeyGateActive = false;
4514
4881
  if (pending.secret.providers.includes(this.sessionState.provider)) {
4515
4882
  if (this.rebuildAgent()) {
4516
4883
  this.resetChatBoxAfterModelSwap();
@@ -4525,12 +4892,14 @@ export class InteractiveShell {
4525
4892
  display.showError(message);
4526
4893
  this.pendingInteraction = null;
4527
4894
  this.pendingSecretRetry = null;
4895
+ this.apiKeyGateActive = false;
4528
4896
  }
4529
- this.terminalInput.render();
4897
+ this.renderPromptArea();
4898
+ this.scheduleQueueProcessing();
4530
4899
  }
4531
- async processRequest(request) {
4900
+ async processRequest(userRequest) {
4532
4901
  if (this.isProcessing) {
4533
- this.enqueueFollowUpAction({ type: 'request', text: request });
4902
+ this.enqueueFollowUpAction({ type: 'request', text: userRequest });
4534
4903
  return;
4535
4904
  }
4536
4905
  if (!this.agent && !this.rebuildAgent()) {
@@ -4541,28 +4910,54 @@ export class InteractiveShell {
4541
4910
  if (!agent) {
4542
4911
  return;
4543
4912
  }
4544
- this.logUserPrompt(request);
4913
+ this.hasShownThoughtProcess = false;
4914
+ const alphaZeroEngaged = this.alphaZeroModeEnabled;
4915
+ const alphaZeroDifficult = alphaZeroEngaged ? this.isDifficultProblem(userRequest) : false;
4916
+ const requestForAgent = alphaZeroEngaged
4917
+ ? this.buildAlphaZeroPrompt(userRequest, alphaZeroDifficult)
4918
+ : userRequest;
4919
+ const skipReflectionForThisRun = this.skipNextAutoReflection;
4920
+ this.skipNextAutoReflection = false;
4921
+ const scrollbackStartIndex = this.terminalInput.getScrollbackBuffer().length;
4922
+ const alphaZeroStatusId = 'alpha-zero';
4923
+ let alphaZeroStatusApplied = false;
4924
+ let alphaZeroTaskStarted = false;
4925
+ let alphaZeroTaskCompleted = false;
4926
+ if (alphaZeroEngaged) {
4927
+ const detail = alphaZeroDifficult ? 'Difficult request detected' : 'Reinforcement loop enabled';
4928
+ this.statusTracker.pushOverride(alphaZeroStatusId, 'AlphaZero RL active', {
4929
+ detail: `${detail} · full verification`,
4930
+ tone: 'info',
4931
+ });
4932
+ alphaZeroStatusApplied = true;
4933
+ display.showInfo(`AlphaZero RL mode engaged${alphaZeroDifficult ? ' for a difficult request' : ''}. Duel + self-critique with verification enabled.`);
4934
+ this.alphaZeroMetrics.startAlphaZeroTask(userRequest);
4935
+ alphaZeroTaskStarted = true;
4936
+ }
4937
+ this.logUserPrompt(userRequest);
4545
4938
  this.isProcessing = true;
4546
4939
  this.uiUpdates.setMode('processing');
4547
4940
  this.terminalInput.setStreaming(true);
4548
4941
  // Keep the persistent input/control bar active as we transition into streaming.
4549
- this.terminalInput.forceRender();
4942
+ this.renderPromptArea(true);
4550
4943
  const requestStartTime = Date.now(); // Alpha Zero 2 timing
4551
4944
  // Clear previous parallel agents and start fresh for new request
4552
4945
  const parallelManager = getParallelAgentManager();
4553
4946
  parallelManager.clear();
4554
4947
  parallelManager.startBatch();
4555
4948
  // AlphaZero: Track task for learning
4556
- this.lastUserQuery = request;
4557
- this.currentTaskType = classifyTaskType(request);
4949
+ this.lastUserQuery = userRequest;
4950
+ this.currentTaskType = classifyTaskType(userRequest);
4558
4951
  this.currentToolCalls = [];
4559
4952
  this.uiAdapter.startProcessing('Working on your request');
4560
4953
  this.setProcessingStatus();
4561
4954
  let responseText = '';
4955
+ let detectedFailure = null;
4956
+ let hadUnhandledError = false;
4562
4957
  try {
4563
4958
  // Start streaming - no header needed, the input area already provides context
4564
- this.startStreamingHeartbeat('Streaming response');
4565
- responseText = await agent.send(request, true);
4959
+ this.startStreamingHeartbeat(alphaZeroEngaged ? 'AlphaZero RL' : 'Streaming response');
4960
+ responseText = await agent.send(requestForAgent, true);
4566
4961
  await this.awaitPendingCleanup();
4567
4962
  this.captureHistorySnapshot();
4568
4963
  this.autosaveIfEnabled();
@@ -4581,14 +4976,18 @@ export class InteractiveShell {
4581
4976
  duration: 0,
4582
4977
  }));
4583
4978
  // AlphaZero: Check for failure in response
4584
- const failure = detectFailure(responseText, {
4979
+ detectedFailure = detectFailure(responseText, {
4585
4980
  toolCalls: this.currentToolCalls,
4586
- userMessage: request,
4981
+ userMessage: userRequest,
4587
4982
  });
4588
- if (failure) {
4589
- this.lastFailure = failure;
4983
+ if (alphaZeroEngaged && alphaZeroTaskStarted && !alphaZeroTaskCompleted) {
4984
+ this.alphaZeroMetrics.completeAlphaZeroTask(!detectedFailure);
4985
+ alphaZeroTaskCompleted = true;
4986
+ }
4987
+ if (detectedFailure) {
4988
+ this.lastFailure = detectedFailure;
4590
4989
  // Check if we have a recovery strategy
4591
- const strategy = findRecoveryStrategy(failure);
4990
+ const strategy = findRecoveryStrategy(detectedFailure);
4592
4991
  if (strategy) {
4593
4992
  display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
4594
4993
  }
@@ -4611,13 +5010,42 @@ export class InteractiveShell {
4611
5010
  }
4612
5011
  }
4613
5012
  catch (error) {
4614
- const handled = this.handleProviderError(error, () => this.processRequest(request));
5013
+ const handled = this.handleProviderError(error, () => this.processRequest(userRequest));
5014
+ hadUnhandledError = !handled;
5015
+ if (alphaZeroEngaged && alphaZeroTaskStarted && !alphaZeroTaskCompleted) {
5016
+ this.alphaZeroMetrics.completeAlphaZeroTask(false);
5017
+ alphaZeroTaskCompleted = true;
5018
+ }
4615
5019
  if (!handled) {
4616
5020
  // Pass full error object for enhanced formatting with stack trace
4617
5021
  display.showError(error instanceof Error ? error.message : String(error), error);
4618
5022
  }
4619
5023
  }
4620
5024
  finally {
5025
+ const runLogEntry = this.buildRunLogEntry(userRequest, responseText, scrollbackStartIndex, {
5026
+ alphaZeroEngaged,
5027
+ alphaZeroDifficult,
5028
+ failureType: detectedFailure?.type ?? (hadUnhandledError ? 'unhandled-error' : null),
5029
+ });
5030
+ this.lastRunLog = runLogEntry;
5031
+ const allowAutoChain = alphaZeroEngaged && this.autoContinueEnabled && isErosolarRepo(this.workingDir);
5032
+ let shouldStopAuto = false;
5033
+ if (this.alphaZeroAutoImproveActive) {
5034
+ shouldStopAuto = this.shouldStopAlphaZeroAutoImprove(responseText, allowAutoChain);
5035
+ if (shouldStopAuto) {
5036
+ this.alphaZeroAutoImproveActive = false;
5037
+ this.alphaZeroAutoImproveIterations = 0;
5038
+ }
5039
+ }
5040
+ if (!skipReflectionForThisRun && !shouldStopAuto) {
5041
+ this.maybeQueueAlphaZeroSelfReflection(runLogEntry, alphaZeroEngaged, allowAutoChain);
5042
+ }
5043
+ if (alphaZeroEngaged && alphaZeroStatusApplied) {
5044
+ this.statusTracker.clearOverride(alphaZeroStatusId);
5045
+ }
5046
+ if (alphaZeroEngaged && alphaZeroTaskStarted && !alphaZeroTaskCompleted) {
5047
+ this.alphaZeroMetrics.completeAlphaZeroTask(false);
5048
+ }
4621
5049
  display.stopThinking(false);
4622
5050
  this.uiUpdates.setMode('processing');
4623
5051
  this.stopStreamingHeartbeat();
@@ -4631,7 +5059,7 @@ export class InteractiveShell {
4631
5059
  // CRITICAL: Ensure readline prompt is active for user input
4632
5060
  // Claude Code style: New prompt naturally appears at bottom
4633
5061
  this.ensureReadlineReady();
4634
- this.terminalInput.render();
5062
+ this.renderPromptArea();
4635
5063
  this.scheduleQueueProcessing();
4636
5064
  this.refreshQueueIndicators();
4637
5065
  }
@@ -4662,6 +5090,7 @@ export class InteractiveShell {
4662
5090
  if (!agent) {
4663
5091
  return;
4664
5092
  }
5093
+ this.hasShownThoughtProcess = false;
4665
5094
  this.isProcessing = true;
4666
5095
  this.uiUpdates.setMode('processing');
4667
5096
  this.terminalInput.setStreaming(true);
@@ -4702,6 +5131,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
4702
5131
  }
4703
5132
  while (iteration < MAX_ITERATIONS) {
4704
5133
  iteration++;
5134
+ this.hasShownThoughtProcess = false;
4705
5135
  display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
4706
5136
  this.updateStatusMessage(`Working on iteration ${iteration}...`);
4707
5137
  try {
@@ -5102,6 +5532,8 @@ What's the next action?`;
5102
5532
  finally {
5103
5533
  this.updateStatusMessage(null);
5104
5534
  this.autoTestInFlight = false;
5535
+ this.terminalInput.resetContentPosition();
5536
+ this.terminalInput.forceRender();
5105
5537
  }
5106
5538
  }
5107
5539
  isBuildToolCall(entry) {
@@ -5208,7 +5640,8 @@ What's the next action?`;
5208
5640
  // Send the error to the agent for fixing
5209
5641
  display.showThinking('Analyzing build errors');
5210
5642
  this.refreshStatusLine(true);
5211
- const response = await this.agent.send(prompt, true);
5643
+ this.hasShownThoughtProcess = false;
5644
+ const response = await this.withStreamingUi('Fixing build errors', () => this.agent.send(prompt, true));
5212
5645
  display.stopThinking();
5213
5646
  this.refreshStatusLine(true);
5214
5647
  if (response) {
@@ -5243,22 +5676,15 @@ What's the next action?`;
5243
5676
  onStreamFallback: (info) => this.handleStreamingFallback(info),
5244
5677
  onAssistantMessage: (content, metadata) => {
5245
5678
  const enriched = this.buildDisplayMetadata(metadata);
5679
+ const parsed = this.splitThinkingResponse(content);
5680
+ const thinking = parsed?.thinking ?? null;
5681
+ const responseContent = parsed ? parsed.response?.trim() ?? '' : content.trim();
5246
5682
  // Update spinner based on message type
5247
5683
  if (metadata.isFinal) {
5684
+ this.presentThoughtProcess(thinking, enriched, { wasStreamed: metadata.wasStreamed });
5248
5685
  // Skip display if content was already streamed to avoid double-display
5249
- if (!metadata.wasStreamed) {
5250
- const parsed = this.splitThinkingResponse(content);
5251
- if (parsed?.thinking) {
5252
- const summary = this.extractThoughtSummary(parsed.thinking);
5253
- if (summary) {
5254
- display.updateThinking(`💭 ${summary}`);
5255
- }
5256
- display.showAssistantMessage(parsed.thinking, { ...enriched, isFinal: false });
5257
- }
5258
- const finalContent = parsed?.response?.trim() || content;
5259
- if (finalContent) {
5260
- display.showAssistantMessage(finalContent, enriched);
5261
- }
5686
+ if (!metadata.wasStreamed && responseContent) {
5687
+ display.showAssistantMessage(responseContent, enriched);
5262
5688
  }
5263
5689
  // Status shown in mode controls bar - no separate status line needed
5264
5690
  display.stopThinking();
@@ -5275,12 +5701,16 @@ What's the next action?`;
5275
5701
  void this.enforceAutoTests('final-response');
5276
5702
  }
5277
5703
  else {
5704
+ if (thinking) {
5705
+ this.presentThoughtProcess(thinking, enriched, { wasStreamed: metadata.wasStreamed });
5706
+ }
5278
5707
  // Non-final message = narrative text before tool calls (Claude Code style)
5279
5708
  // Stop spinner and show the narrative text directly
5280
5709
  display.stopThinking();
5281
5710
  // Skip display if content was already streamed to avoid double-display
5282
5711
  if (!metadata.wasStreamed) {
5283
- display.showNarrative(content.trim());
5712
+ const narrative = parsed?.response ?? content;
5713
+ display.showNarrative(narrative.trim());
5284
5714
  }
5285
5715
  // The isProcessing flag already shows "⏳ Processing..." - no need for duplicate status
5286
5716
  this.requestPromptRefresh();
@@ -5318,19 +5748,19 @@ What's the next action?`;
5318
5748
  this.updateContextUsage(percentage);
5319
5749
  }
5320
5750
  // Ensure prompt remains visible at bottom after context messages
5321
- this.terminalInput.render();
5751
+ this.renderPromptArea();
5322
5752
  },
5323
5753
  onContinueAfterRecovery: () => {
5324
5754
  // Update UI to show we're continuing after context recovery
5325
5755
  display.showSystemMessage(`🔄 Continuing after context recovery...`);
5326
5756
  this.updateStatusMessage('Retrying with reduced context...');
5327
- this.terminalInput.render();
5757
+ this.renderPromptArea();
5328
5758
  },
5329
5759
  onAutoContinue: (attempt, maxAttempts, _message) => {
5330
5760
  // Show auto-continue progress in UI
5331
5761
  display.showSystemMessage(`🔄 Auto-continue (${attempt}/${maxAttempts}): Model expressed intent, prompting to act...`);
5332
5762
  this.updateStatusMessage('Auto-continuing...');
5333
- this.terminalInput.render();
5763
+ this.renderPromptArea();
5334
5764
  },
5335
5765
  onCancelled: () => {
5336
5766
  // Update UI to show operation was cancelled
@@ -5339,7 +5769,7 @@ What's the next action?`;
5339
5769
  this.stopStreamingHeartbeat();
5340
5770
  this.updateStatusMessage(null);
5341
5771
  this.terminalInput.setStreaming(false);
5342
- this.terminalInput.render();
5772
+ this.renderPromptArea();
5343
5773
  },
5344
5774
  onVerificationNeeded: () => {
5345
5775
  void this.enforceAutoBuild('verification');
@@ -5377,7 +5807,7 @@ What's the next action?`;
5377
5807
  resetChatBoxAfterModelSwap() {
5378
5808
  this.updateStatusMessage(null);
5379
5809
  this.terminalInput.setStreaming(false);
5380
- this.terminalInput.render();
5810
+ this.renderPromptArea();
5381
5811
  this.ensureReadlineReady();
5382
5812
  }
5383
5813
  /**
@@ -5463,6 +5893,19 @@ What's the next action?`;
5463
5893
  contextWindowTokens: this.activeContextWindowTokens,
5464
5894
  };
5465
5895
  }
5896
+ presentThoughtProcess(thinking, metadata, options) {
5897
+ if (!thinking || this.hasShownThoughtProcess) {
5898
+ return;
5899
+ }
5900
+ const summary = this.extractThoughtSummary(thinking);
5901
+ if (summary) {
5902
+ display.updateThinking(`💭 ${summary}`);
5903
+ }
5904
+ if (!options?.wasStreamed) {
5905
+ display.showAssistantMessage(thinking, { ...metadata, isFinal: false });
5906
+ }
5907
+ this.hasShownThoughtProcess = true;
5908
+ }
5466
5909
  handleContextTelemetry(metadata, displayMetadata) {
5467
5910
  if (!metadata.isFinal) {
5468
5911
  return null;
@@ -5823,7 +6266,7 @@ What's the next action?`;
5823
6266
  }
5824
6267
  handleAgentSetupError(error, retryAction, providerOverride) {
5825
6268
  this.pendingInteraction = null;
5826
- const provider = providerOverride ?? this.sessionState.provider;
6269
+ const provider = providerOverride === undefined ? this.sessionState.provider : providerOverride;
5827
6270
  const apiKeyIssue = detectApiKeyError(error, provider);
5828
6271
  if (apiKeyIssue) {
5829
6272
  this.handleApiKeyIssue(apiKeyIssue, retryAction);
@@ -5843,7 +6286,8 @@ What's the next action?`;
5843
6286
  this.requestPromptRefresh(true);
5844
6287
  }
5845
6288
  handleProviderError(error, retryAction) {
5846
- const apiKeyIssue = detectApiKeyError(error, this.sessionState.provider);
6289
+ const providerHint = error instanceof MissingSecretError ? null : this.sessionState.provider;
6290
+ const apiKeyIssue = detectApiKeyError(error, providerHint);
5847
6291
  if (!apiKeyIssue) {
5848
6292
  return false;
5849
6293
  }
@@ -5852,13 +6296,19 @@ What's the next action?`;
5852
6296
  }
5853
6297
  handleApiKeyIssue(info, retryAction) {
5854
6298
  const secret = info.secret ?? null;
5855
- const providerLabel = info.provider ? this.providerLabel(info.provider) : 'the selected provider';
6299
+ const providerLabel = info.provider
6300
+ ? this.providerLabel(info.provider)
6301
+ : secret?.providers?.length
6302
+ ? this.providerLabel(secret.providers[0])
6303
+ : null;
6304
+ const targetLabel = providerLabel ?? secret?.label ?? 'this tool';
6305
+ this.apiKeyGateActive = !!secret;
5856
6306
  if (!secret) {
5857
6307
  this.pendingSecretRetry = null;
5858
6308
  const guidance = 'Run "/secrets" to configure the required API key or export it (e.g., EXPORT KEY=value) before launching the CLI.';
5859
6309
  const baseMessage = info.type === 'missing'
5860
- ? `An API key is required before using ${providerLabel}.`
5861
- : `API authentication failed for ${providerLabel}.`;
6310
+ ? `An API key is required before using ${targetLabel}.`
6311
+ : `API authentication failed for ${targetLabel}.`;
5862
6312
  display.showWarning(`${baseMessage} ${guidance}`.trim());
5863
6313
  return;
5864
6314
  }
@@ -5867,8 +6317,8 @@ What's the next action?`;
5867
6317
  display.showWarning(info.message.trim());
5868
6318
  }
5869
6319
  const prefix = isMissing
5870
- ? `${secret.label} is required before you can use ${providerLabel}.`
5871
- : `${secret.label} appears to be invalid for ${providerLabel}.`;
6320
+ ? `${secret.label} is required before you can use ${targetLabel}.`
6321
+ : `${secret.label} appears to be invalid for ${targetLabel}.`;
5872
6322
  display.showWarning(prefix);
5873
6323
  this.pendingSecretRetry = retryAction ?? null;
5874
6324
  this.pendingInteraction = { type: 'secret-input', secret };