erosolar-cli 2.1.175 → 2.1.177

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.
@@ -4,6 +4,7 @@ import { exec } from 'node:child_process';
4
4
  import { promisify } from 'node:util';
5
5
  import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
6
6
  import { join, resolve } from 'node:path';
7
+ import { AgentOrchestrator } from '../core/agentOrchestrator.js';
7
8
  import { display } from '../ui/display.js';
8
9
  import { theme } from '../ui/theme.js';
9
10
  import { getTerminalColumns } from '../ui/layout.js';
@@ -17,7 +18,7 @@ import { detectPromptBlockError, } from '../core/errors/promptBlockErrors.js';
17
18
  import { detectNetworkError } from '../core/errors/networkErrors.js';
18
19
  import { buildWorkspaceContext } from '../workspace.js';
19
20
  import { buildInteractiveSystemPrompt } from './systemPrompt.js';
20
- import { getTaskCompletionDetector, resetTaskCompletionDetector, WRITE_TOOLS, } from './taskCompletionDetector.js';
21
+ import { WRITE_TOOLS } from './taskCompletionDetector.js';
21
22
  import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
22
23
  import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
23
24
  import { loadMcpServers } from '../mcp/config.js';
@@ -2274,14 +2275,17 @@ export class InteractiveShell {
2274
2275
  // Check for continuous/infinite loop commands or auto-escalation to completion mode
2275
2276
  const explicitContinuous = this.isContinuousCommand(trimmed);
2276
2277
  const autoContinuous = this.shouldAutoRunToCompletion(trimmed);
2277
- if (explicitContinuous || autoContinuous) {
2278
- if (autoContinuous && !explicitContinuous) {
2279
- display.showSystemMessage('⚡ Actionable request detected; running continuously until complete (Ctrl+C to stop).');
2280
- }
2278
+ if (explicitContinuous) {
2281
2279
  await this.processContinuousRequest(trimmed);
2282
2280
  this.syncRendererInput();
2283
2281
  return;
2284
2282
  }
2283
+ if (autoContinuous) {
2284
+ display.showSystemMessage('⚡ Actionable request detected; orchestrating until complete (Ctrl+C to stop).');
2285
+ await this.processRequest(trimmed, { orchestrate: true });
2286
+ this.syncRendererInput();
2287
+ return;
2288
+ }
2285
2289
  // Direct execution for all inputs, including multi-line pastes
2286
2290
  await this.processRequest(trimmed);
2287
2291
  this.syncRendererInput();
@@ -5830,7 +5834,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5830
5834
  this.clearInlinePanel();
5831
5835
  this.syncRendererInput();
5832
5836
  }
5833
- async processRequest(request) {
5837
+ async processRequest(request, options) {
5834
5838
  if (this.isProcessing) {
5835
5839
  this.enqueueFollowUpAction({ type: 'request', text: request });
5836
5840
  return;
@@ -5882,10 +5886,23 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5882
5886
  this.beginAiRuntime();
5883
5887
  let responseText = '';
5884
5888
  let autoFollowThrough = null;
5889
+ let orchestratorResult = null;
5890
+ const orchestrate = options?.orchestrate ?? false;
5885
5891
  try {
5886
5892
  // Start streaming - no header needed, the input area already provides context
5887
5893
  this.startStreamingHeartbeat('Streaming response');
5888
- responseText = await agent.send(request, true);
5894
+ if (orchestrate) {
5895
+ const orchestrator = new AgentOrchestrator(agent);
5896
+ orchestratorResult = await orchestrator.runToCompletion(request, {
5897
+ streaming: true,
5898
+ maxPasses: options?.maxPasses ?? 4,
5899
+ enforceActions: true,
5900
+ });
5901
+ responseText = orchestratorResult.finalResponse;
5902
+ }
5903
+ else {
5904
+ responseText = await agent.send(request, true);
5905
+ }
5889
5906
  this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
5890
5907
  await this.awaitPendingCleanup();
5891
5908
  this.captureHistorySnapshot();
@@ -5897,14 +5914,24 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5897
5914
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
5898
5915
  }
5899
5916
  // AlphaZero: Extract and track tool calls from response
5900
- const toolsUsed = this.getExecutedTools(responseText);
5917
+ const toolsUsed = orchestratorResult
5918
+ ? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
5919
+ : this.getExecutedTools(responseText);
5901
5920
  this.currentToolCalls = toolsUsed.map(name => ({
5902
5921
  name,
5903
5922
  arguments: {},
5904
5923
  success: true, // Assume success if we got here
5905
5924
  duration: 0,
5906
5925
  }));
5907
- autoFollowThrough = this.maybeAutoFollowThrough(request, responseText, toolsUsed);
5926
+ if (!orchestrate) {
5927
+ autoFollowThrough = this.maybeAutoFollowThrough(request, responseText, toolsUsed);
5928
+ }
5929
+ if (orchestratorResult && orchestratorResult.exitReason !== 'complete') {
5930
+ const exitDetail = orchestratorResult.exitReason === 'max-passes'
5931
+ ? 'Reached orchestrator pass limit; showing last response.'
5932
+ : 'Received empty replies while orchestrating; showing last response.';
5933
+ display.showSystemMessage(`⚠️ ${exitDetail}`);
5934
+ }
5908
5935
  // AlphaZero: Check for failure in response
5909
5936
  const failure = detectFailure(responseText, {
5910
5937
  toolCalls: this.currentToolCalls,
@@ -5936,7 +5963,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5936
5963
  }
5937
5964
  }
5938
5965
  catch (error) {
5939
- const handled = this.handleProviderError(error, () => this.processRequest(request));
5966
+ const handled = this.handleProviderError(error, () => this.processRequest(request, options));
5940
5967
  if (!handled) {
5941
5968
  // Pass full error object for enhanced formatting with stack trace
5942
5969
  display.showError(error instanceof Error ? error.message : String(error), error);
@@ -5990,7 +6017,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5990
6017
  * Context is automatically managed - overflow errors trigger auto-recovery.
5991
6018
  */
5992
6019
  async processContinuousRequest(initialRequest) {
5993
- const MAX_ITERATIONS = 100; // Safety limit to prevent truly infinite loops
6020
+ const MAX_PASSES = 100; // Safety limit to prevent truly infinite loops
5994
6021
  if (this.isProcessing) {
5995
6022
  this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
5996
6023
  return;
@@ -5999,54 +6026,9 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5999
6026
  display.showWarning('Configure an API key via /secrets before sending requests.');
6000
6027
  return;
6001
6028
  }
6002
- this.inlinePanelScopeActive = false;
6003
- this.clearInlinePanel();
6004
- const agent = this.agent;
6005
- if (!agent) {
6006
- return;
6007
- }
6008
- this.toolsUsedThisRun = [];
6009
- this.currentToolCalls = [];
6010
- this.lastUserQuery = initialRequest;
6011
- this.clearToolUsageMeta();
6012
- this.isProcessing = true;
6013
- this.uiUpdates.setMode('processing');
6014
- this.streamingTokenCount = 0; // Reset token counter for new request
6015
- this.terminalInput.setStreaming(true);
6016
- if (this.suppressNextNetworkReset) {
6017
- this.suppressNextNetworkReset = false;
6018
- }
6019
- else {
6020
- this.resetNetworkRetryState();
6021
- }
6022
- const overallStartTime = Date.now();
6023
- // Clear previous parallel agents and start fresh for continuous mode
6024
- const parallelManager = getParallelAgentManager();
6025
- parallelManager.clear();
6026
- parallelManager.startBatch();
6027
- // Initialize the task completion detector
6028
- const completionDetector = getTaskCompletionDetector();
6029
- completionDetector.reset();
6030
- // Display user prompt in scrollback (Claude Code style)
6031
- this.logUserPrompt(initialRequest);
6032
6029
  display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
6033
- this.uiAdapter.startProcessing('Continuous execution mode');
6034
- this.setProcessingStatus();
6035
- this.beginAiRuntime();
6036
- // No streaming header - just start streaming directly
6037
- this.startStreamingHeartbeat('Streaming');
6038
- let iteration = 0;
6039
- let lastResponse = '';
6040
- let consecutiveNoProgress = 0;
6041
- const MAX_NO_PROGRESS = 5; // Increased to allow more attempts before giving up
6042
- let pendingVerification = false;
6043
- let verificationAttempts = 0;
6044
- const MAX_VERIFICATION_ATTEMPTS = 2;
6045
- try {
6046
- // Enhance initial prompt with git context for self-improvement tasks
6047
- let currentPrompt = initialRequest;
6048
- if (this.isSelfImprovementRequest(initialRequest)) {
6049
- currentPrompt = `${initialRequest}
6030
+ const preparedRequest = this.isSelfImprovementRequest(initialRequest)
6031
+ ? `${initialRequest}
6050
6032
 
6051
6033
  IMPORTANT: You have full git access. After making improvements:
6052
6034
  1. Use bash to run: git status (see changes)
@@ -6055,175 +6037,9 @@ IMPORTANT: You have full git access. After making improvements:
6055
6037
  4. Use bash to run: git push (when milestone reached)
6056
6038
 
6057
6039
  Commit frequently with descriptive messages. Push when ready.
6058
- When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
6059
- }
6060
- while (iteration < MAX_ITERATIONS) {
6061
- iteration++;
6062
- this.toolsUsedThisRun = [];
6063
- display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
6064
- this.updateStatusMessage(`Working on iteration ${iteration}...`);
6065
- try {
6066
- // Send the request and capture the response (streaming disabled)
6067
- display.showThinking('Responding...');
6068
- this.refreshStatusLine(true);
6069
- const response = await agent.send(currentPrompt, true);
6070
- this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
6071
- await this.awaitPendingCleanup();
6072
- this.captureHistorySnapshot();
6073
- this.autosaveIfEnabled();
6074
- // Track metrics
6075
- const elapsedMs = Date.now() - overallStartTime;
6076
- this.alphaZeroMetrics.recordMessage(elapsedMs);
6077
- if (!response?.trim()) {
6078
- display.showWarning('Model returned an empty response. Retrying this iteration...');
6079
- consecutiveNoProgress++;
6080
- currentPrompt = `${initialRequest}
6081
-
6082
- The previous reply was empty. Resume the task now: take the next action, call the necessary tools, and report progress.`;
6083
- continue;
6084
- }
6085
- // Extract tools used from the response (look for tool call patterns)
6086
- const toolsUsed = this.getExecutedTools(response);
6087
- toolsUsed.forEach(tool => completionDetector.recordToolCall(tool, true, true));
6088
- // Use intelligent completion detection
6089
- const completionAnalysis = completionDetector.analyzeCompletion(response, toolsUsed);
6090
- display.showSystemMessage(`📈 Completion confidence: ${(completionAnalysis.confidence * 100).toFixed(0)}%`);
6091
- // Check for explicit TASK_FULLY_COMPLETE marker (highest priority)
6092
- // BUT: Don't terminate if the response also indicates work is incomplete
6093
- if (response.includes('TASK_FULLY_COMPLETE')) {
6094
- const hasContradiction = this.responseIndicatesIncompleteWork(response);
6095
- if (hasContradiction) {
6096
- display.showSystemMessage(`\n⚠️ TASK_FULLY_COMPLETE detected but response indicates incomplete work. Continuing...`);
6097
- // Override the completion signal - the AI is contradicting itself
6098
- currentPrompt = `You marked the task as TASK_FULLY_COMPLETE but also indicated that work is still pending or not integrated. Please clarify:
6099
-
6100
- 1. Is ALL the originally requested work actually complete and functional?
6101
- 2. If there are parts that are "ready but not integrated" or "implemented but not connected", those are NOT complete.
6102
- 3. Only say TASK_FULLY_COMPLETE when the user's original request is 100% fulfilled.
6103
-
6104
- What remains to be done? Continue with the next step.`;
6105
- await new Promise(resolve => setTimeout(resolve, 500));
6106
- continue;
6107
- }
6108
- display.showSystemMessage(`\n✅ Task explicitly marked complete after ${iteration} iteration(s).`);
6109
- break;
6110
- }
6111
- // High confidence completion without verification needed
6112
- if (completionAnalysis.isComplete && completionAnalysis.confidence >= 0.85) {
6113
- display.showSystemMessage(`\n✅ Task completed with high confidence after ${iteration} iteration(s).`);
6114
- display.showSystemMessage(` Reason: ${completionAnalysis.reason}`);
6115
- break;
6116
- }
6117
- // Medium confidence - run verification round
6118
- if (completionAnalysis.shouldVerify && completionAnalysis.verificationPrompt && !pendingVerification) {
6119
- if (verificationAttempts < MAX_VERIFICATION_ATTEMPTS) {
6120
- display.showSystemMessage(`\n🔍 Running verification round (confidence: ${(completionAnalysis.confidence * 100).toFixed(0)}%)...`);
6121
- pendingVerification = true;
6122
- verificationAttempts++;
6123
- currentPrompt = completionAnalysis.verificationPrompt;
6124
- await new Promise(resolve => setTimeout(resolve, 500));
6125
- continue;
6126
- }
6127
- }
6128
- // If we were in verification mode, check the result
6129
- if (pendingVerification) {
6130
- pendingVerification = false;
6131
- if (completionDetector.isVerificationConfirmed(response)) {
6132
- display.showSystemMessage(`\n✅ Task completion verified by AI after ${iteration} iteration(s).`);
6133
- break;
6134
- }
6135
- else {
6136
- display.showSystemMessage(`🔄 Verification indicates more work needed. Continuing...`);
6137
- }
6138
- }
6139
- // Check for no progress (same response multiple times)
6140
- const responseChanged = response !== lastResponse;
6141
- if (!responseChanged) {
6142
- consecutiveNoProgress++;
6143
- if (consecutiveNoProgress >= MAX_NO_PROGRESS) {
6144
- // Before giving up, ask one final verification
6145
- if (verificationAttempts < MAX_VERIFICATION_ATTEMPTS) {
6146
- display.showSystemMessage(`\n⚠️ No progress for ${MAX_NO_PROGRESS} iterations. Running final verification...`);
6147
- currentPrompt = `I notice you may be stuck or finished. Please confirm:
6148
-
6149
- 1. Is the original task FULLY complete?
6150
- 2. If yes, respond with exactly: "TASK_FULLY_COMPLETE"
6151
- 3. If no, what specific action should be taken next?
6152
-
6153
- Be explicit about the current state.`;
6154
- verificationAttempts++;
6155
- consecutiveNoProgress = 0;
6156
- await new Promise(resolve => setTimeout(resolve, 500));
6157
- continue;
6158
- }
6159
- display.showSystemMessage(`\n⚠️ No progress detected for ${MAX_NO_PROGRESS} iterations and verification exhausted. Stopping.`);
6160
- break;
6161
- }
6162
- }
6163
- else {
6164
- consecutiveNoProgress = 0;
6165
- }
6166
- lastResponse = response;
6167
- // Prepare next iteration prompt - explicitly encourage progress reporting
6168
- currentPrompt = `Continue with the next step. Remember:
6169
- - Use bash to run git commands (git status, git add, git commit, git push)
6170
- - Commit your changes with descriptive messages after completing improvements
6171
- - Push changes when a logical milestone is reached
6172
- - If all tasks are complete, respond with exactly: "TASK_FULLY_COMPLETE"
6173
- - If there are errors or blockers, explain what's preventing progress
6174
-
6175
- What's the next action?`;
6176
- // Small delay between iterations to prevent rate limiting
6177
- await new Promise(resolve => setTimeout(resolve, 500));
6178
- }
6179
- catch (error) {
6180
- display.stopThinking(false);
6181
- // Handle context overflow specially - the agent should auto-recover
6182
- // but if it propagates here, we continue the loop
6183
- if (this.isContextOverflowError(error)) {
6184
- display.showSystemMessage(`⚡ Context overflow handled. Continuing with reduced context...`);
6185
- // The agent.ts should have already handled recovery
6186
- // Continue to next iteration
6187
- continue;
6188
- }
6189
- // For other errors, check if handled by provider error handler
6190
- const handled = this.handleProviderError(error, () => this.processContinuousRequest(initialRequest));
6191
- if (!handled) {
6192
- display.showError(error instanceof Error ? error.message : String(error), error);
6193
- break;
6194
- }
6195
- }
6196
- }
6197
- if (iteration >= MAX_ITERATIONS) {
6198
- display.showWarning(`\n⚠️ Reached maximum iterations (${MAX_ITERATIONS}). Stopping to prevent infinite loop.`);
6199
- }
6200
- }
6201
- finally {
6202
- this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
6203
- const totalElapsed = Date.now() - overallStartTime;
6204
- const minutes = Math.floor(totalElapsed / 60000);
6205
- const seconds = Math.floor((totalElapsed % 60000) / 1000);
6206
- display.showSystemMessage(`\n🏁 Continuous execution completed: ${iteration} iterations, ${minutes}m ${seconds}s total`);
6207
- // Reset completion detector for next task
6208
- resetTaskCompletionDetector();
6209
- this.uiUpdates.setMode('processing');
6210
- this.stopStreamingHeartbeat('complete', { quiet: true });
6211
- this.endAiRuntime();
6212
- this.isProcessing = false;
6213
- this.terminalInput.setStreaming(false);
6214
- this.uiAdapter.endProcessing('Ready for prompts');
6215
- this.updateToolUsageMeta(this.uiAdapter.getToolUsageSummary({ plain: true }));
6216
- this.setIdleStatus();
6217
- this.updateStatusMessage(null);
6218
- this.toolsUsedThisRun = [];
6219
- queueMicrotask(() => this.uiUpdates.setMode('idle'));
6220
- // CRITICAL: Ensure readline prompt is active for user input
6221
- // Erosolar-CLI style: New prompt naturally appears at bottom
6222
- this.ensureReadlineReady();
6223
- this.scheduleQueueProcessing();
6224
- this.maybeProcessPromptInbox();
6225
- this.refreshQueueIndicators();
6226
- }
6040
+ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
6041
+ : initialRequest;
6042
+ await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
6227
6043
  }
6228
6044
  /**
6229
6045
  * Resolve executed tools for the current turn. Prefer the actual tool