erosolar-cli 2.1.176 → 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.
- package/dist/core/agent.d.ts +10 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +25 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/core/agentOrchestrator.d.ts +37 -0
- package/dist/core/agentOrchestrator.d.ts.map +1 -0
- package/dist/core/agentOrchestrator.js +147 -0
- package/dist/core/agentOrchestrator.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +10 -1
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +167 -174
- package/dist/shell/interactiveShell.js.map +1 -1
- package/package.json +1 -1
- package/dist/runtime/flowOrchestrator.d.ts +0 -52
- package/dist/runtime/flowOrchestrator.d.ts.map +0 -1
- package/dist/runtime/flowOrchestrator.js +0 -242
- package/dist/runtime/flowOrchestrator.js.map +0 -1
|
@@ -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';
|
|
@@ -12,13 +13,12 @@ import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue
|
|
|
12
13
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
13
14
|
import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
|
|
14
15
|
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 {
|
|
21
|
+
import { 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,7 +124,6 @@ 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();
|
|
128
127
|
maxNetworkRetries = 2;
|
|
129
128
|
statusSubscription = null;
|
|
130
129
|
followUpQueue = [];
|
|
@@ -2276,14 +2275,17 @@ export class InteractiveShell {
|
|
|
2276
2275
|
// Check for continuous/infinite loop commands or auto-escalation to completion mode
|
|
2277
2276
|
const explicitContinuous = this.isContinuousCommand(trimmed);
|
|
2278
2277
|
const autoContinuous = this.shouldAutoRunToCompletion(trimmed);
|
|
2279
|
-
if (explicitContinuous
|
|
2280
|
-
if (autoContinuous && !explicitContinuous) {
|
|
2281
|
-
display.showSystemMessage('⚡ Actionable request detected; running continuously until complete (Ctrl+C to stop).');
|
|
2282
|
-
}
|
|
2278
|
+
if (explicitContinuous) {
|
|
2283
2279
|
await this.processContinuousRequest(trimmed);
|
|
2284
2280
|
this.syncRendererInput();
|
|
2285
2281
|
return;
|
|
2286
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
|
+
}
|
|
2287
2289
|
// Direct execution for all inputs, including multi-line pastes
|
|
2288
2290
|
await this.processRequest(trimmed);
|
|
2289
2291
|
this.syncRendererInput();
|
|
@@ -2358,7 +2360,7 @@ export class InteractiveShell {
|
|
|
2358
2360
|
return false;
|
|
2359
2361
|
}
|
|
2360
2362
|
const mutatingToolUsed = toolsUsed.some((tool) => WRITE_TOOL_NAMES.has(tool.toLowerCase()));
|
|
2361
|
-
const planOnly = this.
|
|
2363
|
+
const planOnly = this.isPlanOnlyResponse(response);
|
|
2362
2364
|
const lowActionDensity = response.split(/\s+/).length < 80;
|
|
2363
2365
|
// If it's clearly a plan-only response, continue regardless of prior tool usage
|
|
2364
2366
|
if (planOnly) {
|
|
@@ -5832,7 +5834,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5832
5834
|
this.clearInlinePanel();
|
|
5833
5835
|
this.syncRendererInput();
|
|
5834
5836
|
}
|
|
5835
|
-
async processRequest(request) {
|
|
5837
|
+
async processRequest(request, options) {
|
|
5836
5838
|
if (this.isProcessing) {
|
|
5837
5839
|
this.enqueueFollowUpAction({ type: 'request', text: request });
|
|
5838
5840
|
return;
|
|
@@ -5884,10 +5886,23 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5884
5886
|
this.beginAiRuntime();
|
|
5885
5887
|
let responseText = '';
|
|
5886
5888
|
let autoFollowThrough = null;
|
|
5889
|
+
let orchestratorResult = null;
|
|
5890
|
+
const orchestrate = options?.orchestrate ?? false;
|
|
5887
5891
|
try {
|
|
5888
5892
|
// Start streaming - no header needed, the input area already provides context
|
|
5889
5893
|
this.startStreamingHeartbeat('Streaming response');
|
|
5890
|
-
|
|
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
|
+
}
|
|
5891
5906
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5892
5907
|
await this.awaitPendingCleanup();
|
|
5893
5908
|
this.captureHistorySnapshot();
|
|
@@ -5899,14 +5914,24 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5899
5914
|
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
5900
5915
|
}
|
|
5901
5916
|
// AlphaZero: Extract and track tool calls from response
|
|
5902
|
-
const toolsUsed =
|
|
5917
|
+
const toolsUsed = orchestratorResult
|
|
5918
|
+
? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
|
|
5919
|
+
: this.getExecutedTools(responseText);
|
|
5903
5920
|
this.currentToolCalls = toolsUsed.map(name => ({
|
|
5904
5921
|
name,
|
|
5905
5922
|
arguments: {},
|
|
5906
5923
|
success: true, // Assume success if we got here
|
|
5907
5924
|
duration: 0,
|
|
5908
5925
|
}));
|
|
5909
|
-
|
|
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
|
+
}
|
|
5910
5935
|
// AlphaZero: Check for failure in response
|
|
5911
5936
|
const failure = detectFailure(responseText, {
|
|
5912
5937
|
toolCalls: this.currentToolCalls,
|
|
@@ -5938,7 +5963,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5938
5963
|
}
|
|
5939
5964
|
}
|
|
5940
5965
|
catch (error) {
|
|
5941
|
-
const handled = this.handleProviderError(error, () => this.processRequest(request));
|
|
5966
|
+
const handled = this.handleProviderError(error, () => this.processRequest(request, options));
|
|
5942
5967
|
if (!handled) {
|
|
5943
5968
|
// Pass full error object for enhanced formatting with stack trace
|
|
5944
5969
|
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
@@ -5992,7 +6017,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
5992
6017
|
* Context is automatically managed - overflow errors trigger auto-recovery.
|
|
5993
6018
|
*/
|
|
5994
6019
|
async processContinuousRequest(initialRequest) {
|
|
5995
|
-
const
|
|
6020
|
+
const MAX_PASSES = 100; // Safety limit to prevent truly infinite loops
|
|
5996
6021
|
if (this.isProcessing) {
|
|
5997
6022
|
this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
|
|
5998
6023
|
return;
|
|
@@ -6001,49 +6026,9 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
|
|
|
6001
6026
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
6002
6027
|
return;
|
|
6003
6028
|
}
|
|
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);
|
|
6034
6029
|
display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
|
|
6035
|
-
this.
|
|
6036
|
-
|
|
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}
|
|
6030
|
+
const preparedRequest = this.isSelfImprovementRequest(initialRequest)
|
|
6031
|
+
? `${initialRequest}
|
|
6047
6032
|
|
|
6048
6033
|
IMPORTANT: You have full git access. After making improvements:
|
|
6049
6034
|
1. Use bash to run: git status (see changes)
|
|
@@ -6052,124 +6037,9 @@ IMPORTANT: You have full git access. After making improvements:
|
|
|
6052
6037
|
4. Use bash to run: git push (when milestone reached)
|
|
6053
6038
|
|
|
6054
6039
|
Commit frequently with descriptive messages. Push when ready.
|
|
6055
|
-
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE"
|
|
6056
|
-
|
|
6057
|
-
|
|
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
|
-
}
|
|
6040
|
+
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
6041
|
+
: initialRequest;
|
|
6042
|
+
await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
|
|
6173
6043
|
}
|
|
6174
6044
|
/**
|
|
6175
6045
|
* Resolve executed tools for the current turn. Prefer the actual tool
|
|
@@ -6191,6 +6061,129 @@ The previous reply was empty. Resume the task now: take the next action, call th
|
|
|
6191
6061
|
}
|
|
6192
6062
|
return this.extractToolsFromResponse(responseText);
|
|
6193
6063
|
}
|
|
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
|
+
}
|
|
6194
6187
|
/**
|
|
6195
6188
|
* Extract tool names from a response by looking for tool call patterns
|
|
6196
6189
|
*/
|