erosolar-cli 2.1.177 → 2.1.179
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/core/agent.d.ts +0 -10
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +0 -25
- package/dist/core/agent.js.map +1 -1
- package/dist/runtime/flowOrchestrator.d.ts +68 -0
- package/dist/runtime/flowOrchestrator.d.ts.map +1 -0
- package/dist/runtime/flowOrchestrator.js +346 -0
- package/dist/runtime/flowOrchestrator.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +1 -10
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +174 -167
- package/dist/shell/interactiveShell.js.map +1 -1
- package/package.json +1 -1
- package/dist/core/agentOrchestrator.d.ts +0 -37
- package/dist/core/agentOrchestrator.d.ts.map +0 -1
- package/dist/core/agentOrchestrator.js +0 -147
- package/dist/core/agentOrchestrator.js.map +0 -1
|
@@ -4,7 +4,6 @@ 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';
|
|
8
7
|
import { display } from '../ui/display.js';
|
|
9
8
|
import { theme } from '../ui/theme.js';
|
|
10
9
|
import { getTerminalColumns } from '../ui/layout.js';
|
|
@@ -13,12 +12,13 @@ import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue
|
|
|
13
12
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
14
13
|
import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
|
|
15
14
|
import { buildEnabledToolSet, evaluateToolPermissions, getToolToggleOptions, } from '../capabilities/toolRegistry.js';
|
|
15
|
+
import { FlowOrchestrator } from '../runtime/flowOrchestrator.js';
|
|
16
16
|
import { detectApiKeyError } from '../core/errors/apiKeyErrors.js';
|
|
17
17
|
import { detectPromptBlockError, } from '../core/errors/promptBlockErrors.js';
|
|
18
18
|
import { detectNetworkError } from '../core/errors/networkErrors.js';
|
|
19
19
|
import { buildWorkspaceContext } from '../workspace.js';
|
|
20
20
|
import { buildInteractiveSystemPrompt } from './systemPrompt.js';
|
|
21
|
-
import { WRITE_TOOLS } from './taskCompletionDetector.js';
|
|
21
|
+
import { getTaskCompletionDetector, resetTaskCompletionDetector, WRITE_TOOLS, } from './taskCompletionDetector.js';
|
|
22
22
|
import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
|
|
23
23
|
import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
|
|
24
24
|
import { loadMcpServers } from '../mcp/config.js';
|
|
@@ -124,6 +124,7 @@ export class InteractiveShell {
|
|
|
124
124
|
uiUpdates;
|
|
125
125
|
_fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
|
|
126
126
|
alphaZeroMetrics; // Alpha Zero 2 performance tracking
|
|
127
|
+
flowOrchestrator = new FlowOrchestrator();
|
|
127
128
|
maxNetworkRetries = 2;
|
|
128
129
|
statusSubscription = null;
|
|
129
130
|
followUpQueue = [];
|
|
@@ -2275,17 +2276,14 @@ export class InteractiveShell {
|
|
|
2275
2276
|
// Check for continuous/infinite loop commands or auto-escalation to completion mode
|
|
2276
2277
|
const explicitContinuous = this.isContinuousCommand(trimmed);
|
|
2277
2278
|
const autoContinuous = this.shouldAutoRunToCompletion(trimmed);
|
|
2278
|
-
if (explicitContinuous) {
|
|
2279
|
+
if (explicitContinuous || autoContinuous) {
|
|
2280
|
+
if (autoContinuous && !explicitContinuous) {
|
|
2281
|
+
display.showSystemMessage('⚡ Actionable request detected; running continuously until complete (Ctrl+C to stop).');
|
|
2282
|
+
}
|
|
2279
2283
|
await this.processContinuousRequest(trimmed);
|
|
2280
2284
|
this.syncRendererInput();
|
|
2281
2285
|
return;
|
|
2282
2286
|
}
|
|
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
|
-
}
|
|
2289
2287
|
// Direct execution for all inputs, including multi-line pastes
|
|
2290
2288
|
await this.processRequest(trimmed);
|
|
2291
2289
|
this.syncRendererInput();
|
|
@@ -2360,7 +2358,7 @@ export class InteractiveShell {
|
|
|
2360
2358
|
return false;
|
|
2361
2359
|
}
|
|
2362
2360
|
const mutatingToolUsed = toolsUsed.some((tool) => WRITE_TOOL_NAMES.has(tool.toLowerCase()));
|
|
2363
|
-
const planOnly = this.isPlanOnlyResponse(response);
|
|
2361
|
+
const planOnly = this.flowOrchestrator.isPlanOnlyResponse(response);
|
|
2364
2362
|
const lowActionDensity = response.split(/\s+/).length < 80;
|
|
2365
2363
|
// If it's clearly a plan-only response, continue regardless of prior tool usage
|
|
2366
2364
|
if (planOnly) {
|
|
@@ -5834,7 +5832,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5834
5832
|
this.clearInlinePanel();
|
|
5835
5833
|
this.syncRendererInput();
|
|
5836
5834
|
}
|
|
5837
|
-
async processRequest(request
|
|
5835
|
+
async processRequest(request) {
|
|
5838
5836
|
if (this.isProcessing) {
|
|
5839
5837
|
this.enqueueFollowUpAction({ type: 'request', text: request });
|
|
5840
5838
|
return;
|
|
@@ -5886,23 +5884,10 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5886
5884
|
this.beginAiRuntime();
|
|
5887
5885
|
let responseText = '';
|
|
5888
5886
|
let autoFollowThrough = null;
|
|
5889
|
-
let orchestratorResult = null;
|
|
5890
|
-
const orchestrate = options?.orchestrate ?? false;
|
|
5891
5887
|
try {
|
|
5892
5888
|
// Start streaming - no header needed, the input area already provides context
|
|
5893
5889
|
this.startStreamingHeartbeat('Streaming response');
|
|
5894
|
-
|
|
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
|
-
}
|
|
5890
|
+
responseText = await agent.send(request, true);
|
|
5906
5891
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5907
5892
|
await this.awaitPendingCleanup();
|
|
5908
5893
|
this.captureHistorySnapshot();
|
|
@@ -5914,24 +5899,14 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5914
5899
|
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
5915
5900
|
}
|
|
5916
5901
|
// AlphaZero: Extract and track tool calls from response
|
|
5917
|
-
const toolsUsed =
|
|
5918
|
-
? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
|
|
5919
|
-
: this.getExecutedTools(responseText);
|
|
5902
|
+
const toolsUsed = this.getExecutedTools(responseText);
|
|
5920
5903
|
this.currentToolCalls = toolsUsed.map(name => ({
|
|
5921
5904
|
name,
|
|
5922
5905
|
arguments: {},
|
|
5923
5906
|
success: true, // Assume success if we got here
|
|
5924
5907
|
duration: 0,
|
|
5925
5908
|
}));
|
|
5926
|
-
|
|
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
|
-
}
|
|
5909
|
+
autoFollowThrough = this.maybeAutoFollowThrough(request, responseText, toolsUsed);
|
|
5935
5910
|
// AlphaZero: Check for failure in response
|
|
5936
5911
|
const failure = detectFailure(responseText, {
|
|
5937
5912
|
toolCalls: this.currentToolCalls,
|
|
@@ -5963,7 +5938,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5963
5938
|
}
|
|
5964
5939
|
}
|
|
5965
5940
|
catch (error) {
|
|
5966
|
-
const handled = this.handleProviderError(error, () => this.processRequest(request
|
|
5941
|
+
const handled = this.handleProviderError(error, () => this.processRequest(request));
|
|
5967
5942
|
if (!handled) {
|
|
5968
5943
|
// Pass full error object for enhanced formatting with stack trace
|
|
5969
5944
|
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
@@ -6017,7 +5992,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
6017
5992
|
* Context is automatically managed - overflow errors trigger auto-recovery.
|
|
6018
5993
|
*/
|
|
6019
5994
|
async processContinuousRequest(initialRequest) {
|
|
6020
|
-
const
|
|
5995
|
+
const MAX_ITERATIONS = 100; // Safety limit to prevent truly infinite loops
|
|
6021
5996
|
if (this.isProcessing) {
|
|
6022
5997
|
this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
|
|
6023
5998
|
return;
|
|
@@ -6026,9 +6001,49 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
6026
6001
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
6027
6002
|
return;
|
|
6028
6003
|
}
|
|
6004
|
+
this.inlinePanelScopeActive = false;
|
|
6005
|
+
this.clearInlinePanel();
|
|
6006
|
+
const agent = this.agent;
|
|
6007
|
+
if (!agent) {
|
|
6008
|
+
return;
|
|
6009
|
+
}
|
|
6010
|
+
this.toolsUsedThisRun = [];
|
|
6011
|
+
this.currentToolCalls = [];
|
|
6012
|
+
this.lastUserQuery = initialRequest;
|
|
6013
|
+
this.clearToolUsageMeta();
|
|
6014
|
+
this.isProcessing = true;
|
|
6015
|
+
this.uiUpdates.setMode('processing');
|
|
6016
|
+
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
6017
|
+
this.terminalInput.setStreaming(true);
|
|
6018
|
+
if (this.suppressNextNetworkReset) {
|
|
6019
|
+
this.suppressNextNetworkReset = false;
|
|
6020
|
+
}
|
|
6021
|
+
else {
|
|
6022
|
+
this.resetNetworkRetryState();
|
|
6023
|
+
}
|
|
6024
|
+
const overallStartTime = Date.now();
|
|
6025
|
+
// Clear previous parallel agents and start fresh for continuous mode
|
|
6026
|
+
const parallelManager = getParallelAgentManager();
|
|
6027
|
+
parallelManager.clear();
|
|
6028
|
+
parallelManager.startBatch();
|
|
6029
|
+
// Initialize the task completion detector
|
|
6030
|
+
const completionDetector = getTaskCompletionDetector();
|
|
6031
|
+
completionDetector.reset();
|
|
6032
|
+
// Display user prompt in scrollback (Claude Code style)
|
|
6033
|
+
this.logUserPrompt(initialRequest);
|
|
6029
6034
|
display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
|
|
6030
|
-
|
|
6031
|
-
|
|
6035
|
+
this.uiAdapter.startProcessing('Continuous execution mode');
|
|
6036
|
+
this.setProcessingStatus();
|
|
6037
|
+
this.beginAiRuntime();
|
|
6038
|
+
// No streaming header - just start streaming directly
|
|
6039
|
+
this.startStreamingHeartbeat('Streaming');
|
|
6040
|
+
this.flowOrchestrator.start(initialRequest);
|
|
6041
|
+
let iteration = 0;
|
|
6042
|
+
try {
|
|
6043
|
+
// Enhance initial prompt with git context for self-improvement tasks
|
|
6044
|
+
let currentPrompt = initialRequest;
|
|
6045
|
+
if (this.isSelfImprovementRequest(initialRequest)) {
|
|
6046
|
+
currentPrompt = `${initialRequest}
|
|
6032
6047
|
|
|
6033
6048
|
IMPORTANT: You have full git access. After making improvements:
|
|
6034
6049
|
1. Use bash to run: git status (see changes)
|
|
@@ -6037,9 +6052,124 @@ IMPORTANT: You have full git access. After making improvements:
|
|
|
6037
6052
|
4. Use bash to run: git push (when milestone reached)
|
|
6038
6053
|
|
|
6039
6054
|
Commit frequently with descriptive messages. Push when ready.
|
|
6040
|
-
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE"
|
|
6041
|
-
|
|
6042
|
-
|
|
6055
|
+
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
|
|
6056
|
+
}
|
|
6057
|
+
while (iteration < MAX_ITERATIONS) {
|
|
6058
|
+
iteration++;
|
|
6059
|
+
this.toolsUsedThisRun = [];
|
|
6060
|
+
display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
|
|
6061
|
+
this.updateStatusMessage(`Working on iteration ${iteration}...`);
|
|
6062
|
+
try {
|
|
6063
|
+
// Send the request and capture the response (streaming disabled)
|
|
6064
|
+
display.showThinking('Responding...');
|
|
6065
|
+
this.refreshStatusLine(true);
|
|
6066
|
+
const response = await agent.send(currentPrompt, true);
|
|
6067
|
+
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
6068
|
+
await this.awaitPendingCleanup();
|
|
6069
|
+
this.captureHistorySnapshot();
|
|
6070
|
+
this.autosaveIfEnabled();
|
|
6071
|
+
// Track metrics
|
|
6072
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
6073
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
6074
|
+
if (!response?.trim()) {
|
|
6075
|
+
display.showWarning('Model returned an empty response. Retrying this iteration...');
|
|
6076
|
+
currentPrompt = `${initialRequest}
|
|
6077
|
+
|
|
6078
|
+
The previous reply was empty. Resume the task now: take the next action, call the necessary tools, and report progress.`;
|
|
6079
|
+
continue;
|
|
6080
|
+
}
|
|
6081
|
+
// Extract tools used from the response (look for tool call patterns)
|
|
6082
|
+
const toolsUsed = this.getExecutedTools(response);
|
|
6083
|
+
toolsUsed.forEach(tool => completionDetector.recordToolCall(tool, true, true));
|
|
6084
|
+
// Use intelligent completion detection
|
|
6085
|
+
const completionAnalysis = completionDetector.analyzeCompletion(response, toolsUsed);
|
|
6086
|
+
display.showSystemMessage(`📈 Completion confidence: ${(completionAnalysis.confidence * 100).toFixed(0)}%`);
|
|
6087
|
+
const decision = this.flowOrchestrator.decide({
|
|
6088
|
+
iteration,
|
|
6089
|
+
response,
|
|
6090
|
+
toolsUsed,
|
|
6091
|
+
completionAnalysis,
|
|
6092
|
+
verificationConfirmed: this.flowOrchestrator.isVerificationPending()
|
|
6093
|
+
? completionDetector.isVerificationConfirmed(response)
|
|
6094
|
+
: false,
|
|
6095
|
+
});
|
|
6096
|
+
if (decision.type === 'stop') {
|
|
6097
|
+
display.showSystemMessage(decision.message);
|
|
6098
|
+
break;
|
|
6099
|
+
}
|
|
6100
|
+
if (decision.type === 'stagnation-stop') {
|
|
6101
|
+
display.showWarning(decision.message);
|
|
6102
|
+
break;
|
|
6103
|
+
}
|
|
6104
|
+
if (decision.type === 'execute-plan') {
|
|
6105
|
+
display.showSystemMessage(decision.message);
|
|
6106
|
+
currentPrompt = decision.prompt;
|
|
6107
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6108
|
+
continue;
|
|
6109
|
+
}
|
|
6110
|
+
if (decision.type === 'verify') {
|
|
6111
|
+
display.showSystemMessage(decision.message);
|
|
6112
|
+
currentPrompt = decision.prompt;
|
|
6113
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6114
|
+
continue;
|
|
6115
|
+
}
|
|
6116
|
+
if (decision.type === 'continue') {
|
|
6117
|
+
if (decision.message) {
|
|
6118
|
+
display.showSystemMessage(decision.message);
|
|
6119
|
+
}
|
|
6120
|
+
currentPrompt = decision.prompt;
|
|
6121
|
+
}
|
|
6122
|
+
// Small delay between iterations to prevent rate limiting
|
|
6123
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6124
|
+
}
|
|
6125
|
+
catch (error) {
|
|
6126
|
+
display.stopThinking(false);
|
|
6127
|
+
// Handle context overflow specially - the agent should auto-recover
|
|
6128
|
+
// but if it propagates here, we continue the loop
|
|
6129
|
+
if (this.isContextOverflowError(error)) {
|
|
6130
|
+
display.showSystemMessage(`⚡ Context overflow handled. Continuing with reduced context...`);
|
|
6131
|
+
// The agent.ts should have already handled recovery
|
|
6132
|
+
// Continue to next iteration
|
|
6133
|
+
continue;
|
|
6134
|
+
}
|
|
6135
|
+
// For other errors, check if handled by provider error handler
|
|
6136
|
+
const handled = this.handleProviderError(error, () => this.processContinuousRequest(initialRequest));
|
|
6137
|
+
if (!handled) {
|
|
6138
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
6139
|
+
break;
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
if (iteration >= MAX_ITERATIONS) {
|
|
6144
|
+
display.showWarning(`\n⚠️ Reached maximum iterations (${MAX_ITERATIONS}). Stopping to prevent infinite loop.`);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
finally {
|
|
6148
|
+
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
6149
|
+
const totalElapsed = Date.now() - overallStartTime;
|
|
6150
|
+
const minutes = Math.floor(totalElapsed / 60000);
|
|
6151
|
+
const seconds = Math.floor((totalElapsed % 60000) / 1000);
|
|
6152
|
+
display.showSystemMessage(`\n🏁 Continuous execution completed: ${iteration} iterations, ${minutes}m ${seconds}s total`);
|
|
6153
|
+
// Reset completion detector for next task
|
|
6154
|
+
resetTaskCompletionDetector();
|
|
6155
|
+
this.uiUpdates.setMode('processing');
|
|
6156
|
+
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
6157
|
+
this.endAiRuntime();
|
|
6158
|
+
this.isProcessing = false;
|
|
6159
|
+
this.terminalInput.setStreaming(false);
|
|
6160
|
+
this.uiAdapter.endProcessing('Ready for prompts');
|
|
6161
|
+
this.updateToolUsageMeta(this.uiAdapter.getToolUsageSummary({ plain: true }));
|
|
6162
|
+
this.setIdleStatus();
|
|
6163
|
+
this.updateStatusMessage(null);
|
|
6164
|
+
this.toolsUsedThisRun = [];
|
|
6165
|
+
queueMicrotask(() => this.uiUpdates.setMode('idle'));
|
|
6166
|
+
// CRITICAL: Ensure readline prompt is active for user input
|
|
6167
|
+
// Erosolar-CLI style: New prompt naturally appears at bottom
|
|
6168
|
+
this.ensureReadlineReady();
|
|
6169
|
+
this.scheduleQueueProcessing();
|
|
6170
|
+
this.maybeProcessPromptInbox();
|
|
6171
|
+
this.refreshQueueIndicators();
|
|
6172
|
+
}
|
|
6043
6173
|
}
|
|
6044
6174
|
/**
|
|
6045
6175
|
* Resolve executed tools for the current turn. Prefer the actual tool
|
|
@@ -6061,129 +6191,6 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6061
6191
|
}
|
|
6062
6192
|
return this.extractToolsFromResponse(responseText);
|
|
6063
6193
|
}
|
|
6064
|
-
/**
|
|
6065
|
-
* Detect plan-only responses that narrate intent without executing actions.
|
|
6066
|
-
*/
|
|
6067
|
-
isPlanOnlyResponse(response) {
|
|
6068
|
-
const normalized = response.trim().toLowerCase();
|
|
6069
|
-
if (!normalized) {
|
|
6070
|
-
return false;
|
|
6071
|
-
}
|
|
6072
|
-
// If the assistant is clearly declaring completion, don't treat it as plan-only
|
|
6073
|
-
const completionGuards = [
|
|
6074
|
-
/\bnothing\s+(left|else)\s+(to\s+do|pending)\b/i,
|
|
6075
|
-
/\b(already|now)\s+(clean|complete|done)\b/i,
|
|
6076
|
-
/\b(no\s+(junk|issues?|changes?)\s+found)\b/i,
|
|
6077
|
-
];
|
|
6078
|
-
if (completionGuards.some((pattern) => pattern.test(response))) {
|
|
6079
|
-
return false;
|
|
6080
|
-
}
|
|
6081
|
-
const planIndicators = [
|
|
6082
|
-
/\bplan\b/i,
|
|
6083
|
-
/\bapproach\b/i,
|
|
6084
|
-
/\bsteps?:\b/i,
|
|
6085
|
-
/\bstep\s+1\b/i,
|
|
6086
|
-
/\bstart by\b/i,
|
|
6087
|
-
/\bfirst[, ]/i,
|
|
6088
|
-
/\bthen\b/i,
|
|
6089
|
-
/\bnext\b/i,
|
|
6090
|
-
/\bi['’]?\s*will\b/i,
|
|
6091
|
-
/\bi['’]?\s*ll\b/i,
|
|
6092
|
-
/\bi['’]?\s*can\b.{0,40}\bthen\b/i,
|
|
6093
|
-
/\bi['’]?\s*(?:will|ll)\s+begin\b/i,
|
|
6094
|
-
];
|
|
6095
|
-
return planIndicators.some((pattern) => pattern.test(response));
|
|
6096
|
-
}
|
|
6097
|
-
/**
|
|
6098
|
-
* Check if a response contains indicators that work is actually incomplete,
|
|
6099
|
-
* even if it also contains TASK_FULLY_COMPLETE marker.
|
|
6100
|
-
* This catches contradictory responses where the AI says "done" but also "not integrated yet".
|
|
6101
|
-
*/
|
|
6102
|
-
responseIndicatesIncompleteWork(response) {
|
|
6103
|
-
// Patterns that indicate work isn't actually complete
|
|
6104
|
-
// Organized by category for maintainability
|
|
6105
|
-
const incompletePatterns = [
|
|
6106
|
-
// === INTEGRATION/DEPLOYMENT STATE ===
|
|
6107
|
-
// "hasn't been integrated/implemented/connected yet"
|
|
6108
|
-
/hasn'?t\s+been\s+(integrated|implemented|connected|deployed|added|completed|tested|verified)\s*(yet|still)?/i,
|
|
6109
|
-
// "not yet integrated/implemented" or "not integrated"
|
|
6110
|
-
/not\s+(yet\s+)?(integrated|implemented|connected|deployed|functional|working|complete|tested|verified)/i,
|
|
6111
|
-
// "ready for integration" = NOT integrated
|
|
6112
|
-
/ready\s+(for|to\s+be)\s+(integration|integrated|connected|deployed|testing|review)/i,
|
|
6113
|
-
// "needs to be integrated"
|
|
6114
|
-
/needs?\s+to\s+be\s+(integrated|connected|deployed|added|hooked|wired|tested|reviewed|merged)/i,
|
|
6115
|
-
// Passive voice: "was not performed/completed"
|
|
6116
|
-
/was\s+not\s+(performed|completed|implemented|deployed|integrated|tested)/i,
|
|
6117
|
-
// "the [X] service hasn't been"
|
|
6118
|
-
/the\s+\w+\s+(service|module|component|feature)\s+hasn'?t\s+been/i,
|
|
6119
|
-
// === PARTIAL/INCOMPLETE STATE ===
|
|
6120
|
-
// "still stores/uses/has" (current bad state persists)
|
|
6121
|
-
/still\s+(stores?|uses?|has|contains?|needs?|requires?|missing|lacks?|broken)/i,
|
|
6122
|
-
// Partial completion: "partially", "mostly", "almost"
|
|
6123
|
-
/\b(partially|mostly|almost|nearly|not\s+fully)\s+(complete|done|finished|implemented|working)/i,
|
|
6124
|
-
// Explicit partial: "part of", "some of", "half of"
|
|
6125
|
-
/\b(only\s+)?(part|some|half|portion)\s+of\s+(the\s+)?(task|work|feature|implementation)/i,
|
|
6126
|
-
// === QUALIFIER WORDS (uncertain completion) ===
|
|
6127
|
-
// "should be complete", "appears complete", "theoretically"
|
|
6128
|
-
/\b(should|might|may|could|appears?\s+to)\s+be\s+(complete|done|working|functional)/i,
|
|
6129
|
-
/\btheoretically\s+(complete|done|working|functional)/i,
|
|
6130
|
-
// "assuming", "if everything works"
|
|
6131
|
-
/\b(assuming|provided|if)\s+(everything|it|this|that)\s+(works?|is\s+correct)/i,
|
|
6132
|
-
// === SELF-CONTRADICTION PHRASES ===
|
|
6133
|
-
// "done but...", "complete except...", "finished however..."
|
|
6134
|
-
/\b(done|complete|finished)\s+(but|except|however|although|though)/i,
|
|
6135
|
-
// "however" followed by incomplete indicator
|
|
6136
|
-
/however[,\s].{0,50}?(hasn'?t|not\s+yet|still\s+needs?|pending|remains?|missing|broken|failing)/i,
|
|
6137
|
-
// "but" followed by negative state
|
|
6138
|
-
/\bbut\s+.{0,30}?(not|hasn'?t|won'?t|can'?t|doesn'?t|isn'?t|wasn'?t)/i,
|
|
6139
|
-
// === FUTURE TENSE / DEFERRED WORK ===
|
|
6140
|
-
// "will need to", "will require"
|
|
6141
|
-
/will\s+(need\s+to|require|have\s+to)\s+(integrate|connect|deploy|complete|implement|test|fix)/i,
|
|
6142
|
-
// Deferred: "left as", "deferred", "postponed", "out of scope"
|
|
6143
|
-
/\b(left\s+as|deferred|postponed|out\s+of\s+scope|for\s+later|in\s+a\s+future)/i,
|
|
6144
|
-
// Time-dependent: "after restart", "takes effect after", "once you"
|
|
6145
|
-
/\b(after\s+(restart|reboot|redeploy)|takes?\s+effect\s+after|once\s+you)/i,
|
|
6146
|
-
// === REMAINING WORK INDICATORS ===
|
|
6147
|
-
// "remaining tasks", "outstanding items"
|
|
6148
|
-
/\b(remaining|outstanding|pending|leftover)\s+(tasks?|items?|work|issues?|steps?)/i,
|
|
6149
|
-
// "X more to do", "still have to"
|
|
6150
|
-
/\b(more\s+to\s+do|still\s+have\s+to|yet\s+to\s+be\s+done)/i,
|
|
6151
|
-
// Explicit blockers
|
|
6152
|
-
/\b(blocker|blocked\s+by|waiting\s+(for|on)|depends?\s+on)/i,
|
|
6153
|
-
// === ERROR/FAILURE STATE ===
|
|
6154
|
-
// "failing tests", "build errors"
|
|
6155
|
-
/\b(failing|broken|erroring)\s+(tests?|builds?|checks?|validations?)/i,
|
|
6156
|
-
// "tests? (are )?(still )?failing"
|
|
6157
|
-
/\btests?\s+(are\s+)?(still\s+)?failing/i,
|
|
6158
|
-
// "errors? to (address|fix)"
|
|
6159
|
-
/\b(errors?|warnings?|issues?)\s+to\s+(address|fix|resolve)/i,
|
|
6160
|
-
// "doesn't work", "isn't working", "not working"
|
|
6161
|
-
/\b(doesn'?t|isn'?t|not)\s+(work|working|functional|functioning)/i,
|
|
6162
|
-
// === MANUAL STEPS REQUIRED ===
|
|
6163
|
-
// "you'll need to", "manually run", "requires user"
|
|
6164
|
-
/\b(you('ll|\s+will)\s+need\s+to|manually\s+(run|configure|set|update)|requires?\s+user)/i,
|
|
6165
|
-
// "run this command", "execute the following"
|
|
6166
|
-
/\b(run\s+this|execute\s+the\s+following|apply\s+the\s+migration)/i,
|
|
6167
|
-
// === TODO/FIXME IN PROSE ===
|
|
6168
|
-
// TODO or FIXME mentioned as remaining work (not in code blocks)
|
|
6169
|
-
/\b(todo|fixme|hack|xxx):\s/i,
|
|
6170
|
-
// "need to add", "should implement"
|
|
6171
|
-
/\b(need\s+to|should|must)\s+(add|implement|create|write|build|fix)\b/i,
|
|
6172
|
-
// === SCOPE LIMITATIONS ===
|
|
6173
|
-
// "didn't have time", "ran out of time"
|
|
6174
|
-
/\b(didn'?t|did\s+not)\s+have\s+(time|chance|opportunity)/i,
|
|
6175
|
-
// "beyond scope", "outside scope"
|
|
6176
|
-
/\b(beyond|outside)\s+(the\s+)?scope/i,
|
|
6177
|
-
// "for now" (temporary state)
|
|
6178
|
-
/\b(for\s+now|at\s+this\s+point|currently)\s*.{0,20}?(not|without|lacks?|missing)/i,
|
|
6179
|
-
];
|
|
6180
|
-
for (const pattern of incompletePatterns) {
|
|
6181
|
-
if (pattern.test(response)) {
|
|
6182
|
-
return true;
|
|
6183
|
-
}
|
|
6184
|
-
}
|
|
6185
|
-
return false;
|
|
6186
|
-
}
|
|
6187
6194
|
/**
|
|
6188
6195
|
* Extract tool names from a response by looking for tool call patterns
|
|
6189
6196
|
*/
|