erosolar-cli 2.1.187 → 2.1.189
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/README.md +2 -0
- package/dist/contracts/models.schema.json +9 -0
- package/dist/core/agent.d.ts +11 -11
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +31 -84
- package/dist/core/agent.js.map +1 -1
- package/dist/core/agentOrchestrator.d.ts +49 -0
- package/dist/core/agentOrchestrator.d.ts.map +1 -0
- package/dist/core/agentOrchestrator.js +313 -0
- package/dist/core/agentOrchestrator.js.map +1 -0
- package/dist/core/schemaValidator.d.ts +5 -0
- package/dist/core/schemaValidator.d.ts.map +1 -1
- package/dist/core/schemaValidator.js +65 -0
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/taskCompletionDetector.d.ts.map +1 -0
- package/dist/{shell → core}/taskCompletionDetector.js +1 -1
- package/dist/core/taskCompletionDetector.js.map +1 -0
- package/dist/core/toolRuntime.d.ts +4 -0
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +40 -64
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +18 -3
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +395 -222
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.js +8 -1
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/tools/bashTools.d.ts +0 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +3 -47
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/buildTools.js +1 -1
- package/dist/tools/buildTools.js.map +1 -1
- package/dist/tools/grepTools.js +6 -4
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/repoChecksTools.d.ts.map +1 -1
- package/dist/tools/repoChecksTools.js +5 -7
- package/dist/tools/repoChecksTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +3 -2
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +9 -7
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/package.json +1 -1
- package/dist/runtime/flowOrchestrator.d.ts +0 -68
- package/dist/runtime/flowOrchestrator.d.ts.map +0 -1
- package/dist/runtime/flowOrchestrator.js +0 -371
- package/dist/runtime/flowOrchestrator.js.map +0 -1
- package/dist/shell/taskCompletionDetector.d.ts.map +0 -1
- package/dist/shell/taskCompletionDetector.js.map +0 -1
- /package/dist/{shell → core}/taskCompletionDetector.d.ts +0 -0
|
@@ -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,11 @@ 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 { getTaskCompletionDetector, resetTaskCompletionDetector, WRITE_TOOLS, } from './taskCompletionDetector.js';
|
|
22
21
|
import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
|
|
23
22
|
import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
|
|
24
23
|
import { loadMcpServers } from '../mcp/config.js';
|
|
@@ -86,10 +85,6 @@ const CONTEXT_CLEANUP_SYSTEM_PROMPT = `Summarize earlier IDE collaboration so th
|
|
|
86
85
|
- Respond in plain Markdown only (no tool or shell calls).`;
|
|
87
86
|
const MAX_ATTACHMENT_BYTES = 200 * 1024; // 200KB per attachment
|
|
88
87
|
const MAX_ATTACHMENT_CHARS = 16000; // Guardrail to avoid flooding context
|
|
89
|
-
const WRITE_TOOL_NAMES = new Set(Array.from(WRITE_TOOLS)
|
|
90
|
-
.map((tool) => tool.toLowerCase())
|
|
91
|
-
// Bash/execute commands can be read-only; guard only on clear mutating tools
|
|
92
|
-
.filter((tool) => !tool.includes('bash') && !tool.startsWith('execute')));
|
|
93
88
|
export class InteractiveShell {
|
|
94
89
|
agent = null;
|
|
95
90
|
profile;
|
|
@@ -124,7 +119,6 @@ export class InteractiveShell {
|
|
|
124
119
|
uiUpdates;
|
|
125
120
|
_fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
|
|
126
121
|
alphaZeroMetrics; // Alpha Zero 2 performance tracking
|
|
127
|
-
flowOrchestrator = new FlowOrchestrator();
|
|
128
122
|
maxNetworkRetries = 2;
|
|
129
123
|
statusSubscription = null;
|
|
130
124
|
followUpQueue = [];
|
|
@@ -140,6 +134,7 @@ export class InteractiveShell {
|
|
|
140
134
|
lastContextWarningLevel = null;
|
|
141
135
|
sessionPreferences;
|
|
142
136
|
autosaveEnabled;
|
|
137
|
+
orchestrationNoticeShown = false;
|
|
143
138
|
verificationEnabled = false;
|
|
144
139
|
criticalApprovalMode = 'auto';
|
|
145
140
|
editGuardMode = 'display-edits';
|
|
@@ -2276,14 +2271,17 @@ export class InteractiveShell {
|
|
|
2276
2271
|
// Check for continuous/infinite loop commands or auto-escalation to completion mode
|
|
2277
2272
|
const explicitContinuous = this.isContinuousCommand(trimmed);
|
|
2278
2273
|
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
|
-
}
|
|
2274
|
+
if (explicitContinuous) {
|
|
2283
2275
|
await this.processContinuousRequest(trimmed);
|
|
2284
2276
|
this.syncRendererInput();
|
|
2285
2277
|
return;
|
|
2286
2278
|
}
|
|
2279
|
+
if (autoContinuous) {
|
|
2280
|
+
display.showSystemMessage('⚡ Actionable request detected; orchestrating until complete (Ctrl+C to stop).');
|
|
2281
|
+
await this.processRequest(trimmed, { orchestrate: true });
|
|
2282
|
+
this.syncRendererInput();
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2287
2285
|
// Direct execution for all inputs, including multi-line pastes
|
|
2288
2286
|
await this.processRequest(trimmed);
|
|
2289
2287
|
this.syncRendererInput();
|
|
@@ -2320,19 +2318,113 @@ export class InteractiveShell {
|
|
|
2320
2318
|
];
|
|
2321
2319
|
const strongMaintenanceIntent = maintenancePatterns.some((pattern) => pattern.test(normalized));
|
|
2322
2320
|
// General action intent in a code context
|
|
2323
|
-
const actionVerb = /\b(fix|resolve|address|refactor|implement|upgrade|migrate|optimi[sz]e|modernize|stabilize|harden|ship|complete|finish|clean|remove|delete|prune|tidy|audit|cleanup)\b/;
|
|
2321
|
+
const actionVerb = /\b(fix|resolve|address|refactor|implement|upgrade|migrate|optimi[sz]e|modernize|stabilize|harden|ship|complete|finish|clean|remove|delete|prune|tidy|audit|cleanup|debug|investigate|triage|scan|check|diagnos(e|is))\b/;
|
|
2324
2322
|
const codeContext = /\b(repo|codebase|project|app|service|package|module|component|workspace|cli|tests?|source|files?|artifacts?)\b/;
|
|
2325
|
-
const
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2323
|
+
const hasActionVerb = actionVerb.test(normalized);
|
|
2324
|
+
const hasCodeContext = codeContext.test(normalized);
|
|
2325
|
+
// Bug/issue hunt or health-check intents, even when phrased as questions
|
|
2326
|
+
const bugSweepIntent = /\b(got\s+any|any|open|known)\s+(bugs?|issues?|defects?)\b/;
|
|
2327
|
+
const triageIntent = /\b(find|check|hunt|triage|detect|scan|review|look\s+for)\s+(bugs?|issues?|errors?|failures?)\b/;
|
|
2328
|
+
const failingTestsIntent = /\b(failing|broken|red)\s+tests?\b|\btests?\s+(are\s+)?failing\b/;
|
|
2329
|
+
const qualitySweepIntent = /\b(audit|health\s+check|stability\s+check|bug\s+scan|issue\s+scan|regression\s+pass)\b/;
|
|
2330
|
+
const bugOrHealthIntent = [bugSweepIntent, triageIntent, failingTestsIntent, qualitySweepIntent].some((pattern) => pattern.test(normalized));
|
|
2331
|
+
// Score-based intent detection keeps questions like "what is this repo?" out
|
|
2332
|
+
let actionScore = 0;
|
|
2333
|
+
if (strongMaintenanceIntent)
|
|
2334
|
+
actionScore += 2;
|
|
2335
|
+
if (bugOrHealthIntent)
|
|
2336
|
+
actionScore += 2;
|
|
2337
|
+
if (hasActionVerb)
|
|
2338
|
+
actionScore += 1;
|
|
2339
|
+
if (hasCodeContext)
|
|
2340
|
+
actionScore += 1;
|
|
2341
|
+
const decisiveAction = actionScore >= 2 || (actionScore === 1 && !isQuestion && normalized.length > 10);
|
|
2342
|
+
if (!decisiveAction) {
|
|
2343
|
+
return false;
|
|
2344
|
+
}
|
|
2345
|
+
// Respect informational openers unless intent is clearly actionable
|
|
2346
|
+
if (startsWithInfoWord && actionScore < 3 && !bugOrHealthIntent && !strongMaintenanceIntent) {
|
|
2347
|
+
return false;
|
|
2348
|
+
}
|
|
2349
|
+
return true;
|
|
2350
|
+
}
|
|
2351
|
+
shouldContinueOrchestrating(originalRequest, result) {
|
|
2352
|
+
if (!result) {
|
|
2353
|
+
return { shouldContinue: false, reason: null };
|
|
2354
|
+
}
|
|
2355
|
+
const actionable = this.shouldAutoRunToCompletion(originalRequest);
|
|
2356
|
+
if (!actionable) {
|
|
2357
|
+
const incomplete = result.exitReason !== 'complete';
|
|
2358
|
+
return { shouldContinue: incomplete, reason: incomplete ? result.exitReason : null };
|
|
2359
|
+
}
|
|
2360
|
+
if (result.exitReason !== 'complete') {
|
|
2361
|
+
return { shouldContinue: true, reason: result.exitReason };
|
|
2362
|
+
}
|
|
2363
|
+
const executedTools = result.passes.some(pass => (pass.toolsUsed?.length ?? 0) > 0);
|
|
2364
|
+
const lastPass = result.passes[result.passes.length - 1];
|
|
2365
|
+
const lastPlanOnly = Boolean(lastPass?.planOnly) && !lastPass?.tookAction && (!lastPass?.toolsUsed?.length);
|
|
2366
|
+
const lastEmpty = !lastPass?.response?.trim();
|
|
2367
|
+
if (!executedTools) {
|
|
2368
|
+
return { shouldContinue: true, reason: 'no-action' };
|
|
2369
|
+
}
|
|
2370
|
+
if (lastPlanOnly) {
|
|
2371
|
+
return { shouldContinue: true, reason: 'plan-only' };
|
|
2372
|
+
}
|
|
2373
|
+
if (lastEmpty) {
|
|
2374
|
+
return { shouldContinue: true, reason: 'empty-response' };
|
|
2375
|
+
}
|
|
2376
|
+
return { shouldContinue: false, reason: null };
|
|
2377
|
+
}
|
|
2378
|
+
describeContinuationReason(reason) {
|
|
2379
|
+
switch (reason) {
|
|
2380
|
+
case 'no-action':
|
|
2381
|
+
return 'No tools or concrete actions were executed; continuing until real work is done.';
|
|
2382
|
+
case 'plan-only':
|
|
2383
|
+
return 'Last pass was just planning; executing the next concrete step now.';
|
|
2384
|
+
case 'empty-response':
|
|
2385
|
+
case 'empty':
|
|
2386
|
+
return 'Previous pass returned nothing; resuming with a concrete action.';
|
|
2387
|
+
case 'stalled':
|
|
2388
|
+
return 'Previous orchestration stalled; forcing continuation.';
|
|
2389
|
+
case 'max-passes':
|
|
2390
|
+
return 'Reached pass limit; extending the run to finish the task.';
|
|
2391
|
+
default:
|
|
2392
|
+
return 'Continuing orchestration until the task is actually finished.';
|
|
2333
2393
|
}
|
|
2334
|
-
return false;
|
|
2335
2394
|
}
|
|
2395
|
+
buildForcedContinuationPrompt(originalRequest, lastResult, continuationReason) {
|
|
2396
|
+
const lastPass = lastResult.passes[lastResult.passes.length - 1];
|
|
2397
|
+
const rawResponse = lastPass?.response?.trim() ?? '';
|
|
2398
|
+
const truncatedResponse = rawResponse ? rawResponse.slice(0, 1200) : '';
|
|
2399
|
+
const responseNote = rawResponse && rawResponse.length > truncatedResponse.length
|
|
2400
|
+
? '\n\n[Last response truncated]'
|
|
2401
|
+
: '';
|
|
2402
|
+
const usedTools = lastResult.passes.flatMap(pass => pass.toolsUsed ?? []);
|
|
2403
|
+
const hasToolUsage = usedTools.length > 0;
|
|
2404
|
+
const toolsUsed = hasToolUsage
|
|
2405
|
+
? `Tools used so far: ${usedTools.join(', ')}.`
|
|
2406
|
+
: 'No tools have been used yet - start with a quick workspace scan (list_files + glob/grep/search) and then take concrete actions now.';
|
|
2407
|
+
const responseSection = truncatedResponse
|
|
2408
|
+
? `Last response snapshot:\n${truncatedResponse}${responseNote}\n`
|
|
2409
|
+
: '';
|
|
2410
|
+
const exitReason = continuationReason ?? lastResult.exitReason ?? 'incomplete';
|
|
2411
|
+
const reasonLine = this.describeContinuationReason(exitReason);
|
|
2412
|
+
const missingActionLine = hasToolUsage
|
|
2413
|
+
? ''
|
|
2414
|
+
: 'No tool calls or file edits have been made yet. Start with list_files + glob/grep/search to sweep the repo, then run a command (read/search/test/edit) and show the output before summarizing.';
|
|
2415
|
+
return `${originalRequest.trim()}
|
|
2416
|
+
|
|
2417
|
+
The previous orchestration stopped early (reason: ${exitReason}). Continue working until the task is truly finished. Use tools, run checks, and only declare completion when nothing remains.
|
|
2418
|
+
|
|
2419
|
+
${reasonLine}
|
|
2420
|
+
${toolsUsed}
|
|
2421
|
+
${responseSection}
|
|
2422
|
+
${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concrete action now.`;
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Decide whether to automatically continue execution when the model stops after a plan/summary
|
|
2426
|
+
* without taking actions. This keeps flows moving toward completion instead of stalling on planning.
|
|
2427
|
+
*/
|
|
2336
2428
|
isExitCommand(input) {
|
|
2337
2429
|
const lower = input.trim().toLowerCase();
|
|
2338
2430
|
return (lower === 'exit' ||
|
|
@@ -5780,22 +5872,20 @@ export class InteractiveShell {
|
|
|
5780
5872
|
this.clearInlinePanel();
|
|
5781
5873
|
this.syncRendererInput();
|
|
5782
5874
|
}
|
|
5783
|
-
async
|
|
5784
|
-
const { maxIterations, mode } = options;
|
|
5785
|
-
const followUpType = mode === 'continuous' ? 'continuous' : 'request';
|
|
5875
|
+
async processRequest(request, options) {
|
|
5786
5876
|
if (this.isProcessing) {
|
|
5787
|
-
this.enqueueFollowUpAction({ type:
|
|
5788
|
-
return
|
|
5877
|
+
this.enqueueFollowUpAction({ type: 'request', text: request });
|
|
5878
|
+
return;
|
|
5789
5879
|
}
|
|
5790
5880
|
if (!this.agent && !this.rebuildAgent()) {
|
|
5791
5881
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
5792
|
-
return
|
|
5882
|
+
return;
|
|
5793
5883
|
}
|
|
5794
5884
|
this.inlinePanelScopeActive = false;
|
|
5795
5885
|
this.clearInlinePanel();
|
|
5796
5886
|
const agent = this.agent;
|
|
5797
5887
|
if (!agent) {
|
|
5798
|
-
return
|
|
5888
|
+
return;
|
|
5799
5889
|
}
|
|
5800
5890
|
this.toolsUsedThisRun = [];
|
|
5801
5891
|
this.currentToolCalls = [];
|
|
@@ -5806,173 +5896,166 @@ export class InteractiveShell {
|
|
|
5806
5896
|
else {
|
|
5807
5897
|
this.resetNetworkRetryState();
|
|
5808
5898
|
}
|
|
5899
|
+
// Reset per-request render tracking
|
|
5809
5900
|
this.responseRendered = false;
|
|
5810
|
-
if (this.shouldLogPrompt(
|
|
5811
|
-
this.logUserPrompt(
|
|
5901
|
+
if (this.shouldLogPrompt(request)) {
|
|
5902
|
+
this.logUserPrompt(request);
|
|
5812
5903
|
}
|
|
5813
5904
|
this.isProcessing = true;
|
|
5814
5905
|
this.uiUpdates.setMode('processing');
|
|
5815
|
-
this.streamingTokenCount = 0;
|
|
5906
|
+
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5816
5907
|
this.terminalInput.setStreaming(true);
|
|
5908
|
+
// Keep the persistent input/control bar active as we transition into streaming.
|
|
5817
5909
|
this.syncRendererInput();
|
|
5818
5910
|
this.renderer?.render();
|
|
5819
|
-
const
|
|
5911
|
+
const requestStartTime = Date.now(); // Alpha Zero 2 timing
|
|
5912
|
+
// Clear previous parallel agents and start fresh for new request
|
|
5820
5913
|
const parallelManager = getParallelAgentManager();
|
|
5821
5914
|
parallelManager.clear();
|
|
5822
5915
|
parallelManager.startBatch();
|
|
5823
|
-
|
|
5824
|
-
this.
|
|
5916
|
+
// AlphaZero: Track task for learning
|
|
5917
|
+
this.lastUserQuery = request;
|
|
5918
|
+
this.currentTaskType = classifyTaskType(request);
|
|
5825
5919
|
this.currentToolCalls = [];
|
|
5826
5920
|
this.clearToolUsageMeta();
|
|
5827
5921
|
this.renderer?.setActivity('Starting...');
|
|
5828
|
-
this.uiAdapter.startProcessing(
|
|
5922
|
+
this.uiAdapter.startProcessing('Working on your request');
|
|
5829
5923
|
this.setProcessingStatus();
|
|
5830
5924
|
this.beginAiRuntime();
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
if (
|
|
5836
|
-
display.showSystemMessage('
|
|
5837
|
-
|
|
5838
|
-
else {
|
|
5839
|
-
display.showSystemMessage('Flow orchestrator engaged; running until the request is satisfied.');
|
|
5840
|
-
}
|
|
5841
|
-
const informationalGuardrail = this.flowOrchestrator.isInformationalRequest(initialRequest)
|
|
5842
|
-
? 'This is an informational request. Keep actions lightweight: avoid running full test/build/lint suites unless explicitly required to answer the question or to confirm a change you made. Prefer quick reads/searches and summarize findings instead of long validations.'
|
|
5843
|
-
: '';
|
|
5844
|
-
const applyInformationalGuardrail = (prompt) => {
|
|
5845
|
-
if (!informationalGuardrail) {
|
|
5846
|
-
return prompt;
|
|
5847
|
-
}
|
|
5848
|
-
return prompt.includes(informationalGuardrail)
|
|
5849
|
-
? prompt
|
|
5850
|
-
: `${prompt}\n\n${informationalGuardrail}`;
|
|
5851
|
-
};
|
|
5852
|
-
let currentPrompt = applyInformationalGuardrail(initialRequest);
|
|
5853
|
-
if (this.isSelfImprovementRequest(initialRequest)) {
|
|
5854
|
-
currentPrompt = applyInformationalGuardrail(`${initialRequest}
|
|
5855
|
-
|
|
5856
|
-
IMPORTANT: You have full git access. After making improvements:
|
|
5857
|
-
1. Use bash to run: git status (see changes)
|
|
5858
|
-
2. Use bash to run: git add -A (stage changes)
|
|
5859
|
-
3. Use bash to run: git commit -m "descriptive message" (commit)
|
|
5860
|
-
4. Use bash to run: git push (when milestone reached)
|
|
5861
|
-
|
|
5862
|
-
Commit frequently with descriptive messages. Push when ready.
|
|
5863
|
-
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`);
|
|
5925
|
+
let responseText = '';
|
|
5926
|
+
let orchestratorResult = null;
|
|
5927
|
+
const orchestrate = options?.orchestrate ?? true;
|
|
5928
|
+
const orchestratorPassLimit = options?.maxPasses ?? 10;
|
|
5929
|
+
if (orchestrate && !this.orchestrationNoticeShown) {
|
|
5930
|
+
display.showSystemMessage('⚡ Orchestrating every prompt until completion. Press Ctrl+C to stop a run early.');
|
|
5931
|
+
this.orchestrationNoticeShown = true;
|
|
5864
5932
|
}
|
|
5865
|
-
let iteration = 0;
|
|
5866
|
-
let lastResponseText = '';
|
|
5867
|
-
let lastToolsUsed = [];
|
|
5868
|
-
let result = null;
|
|
5869
5933
|
try {
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
const
|
|
5885
|
-
|
|
5886
|
-
if (!response?.trim()) {
|
|
5887
|
-
display.showWarning('Model returned an empty response. Retrying this iteration...');
|
|
5888
|
-
currentPrompt = applyInformationalGuardrail(`${initialRequest}
|
|
5889
|
-
|
|
5890
|
-
The previous reply was empty. Resume the task now: take the next action, call the necessary tools, and report progress.`);
|
|
5891
|
-
continue;
|
|
5892
|
-
}
|
|
5893
|
-
const toolsUsed = this.getExecutedTools(response);
|
|
5894
|
-
lastToolsUsed = toolsUsed;
|
|
5895
|
-
toolsUsed.forEach(tool => completionDetector.recordToolCall(tool, true, true));
|
|
5896
|
-
const completionAnalysis = completionDetector.analyzeCompletion(response, toolsUsed);
|
|
5897
|
-
display.showSystemMessage(`📈 Completion confidence: ${(completionAnalysis.confidence * 100).toFixed(0)}%`);
|
|
5898
|
-
const decision = this.flowOrchestrator.decide({
|
|
5899
|
-
iteration,
|
|
5900
|
-
response,
|
|
5901
|
-
toolsUsed,
|
|
5902
|
-
completionAnalysis,
|
|
5903
|
-
verificationConfirmed: this.flowOrchestrator.isVerificationPending()
|
|
5904
|
-
? completionDetector.isVerificationConfirmed(response)
|
|
5905
|
-
: false,
|
|
5906
|
-
});
|
|
5907
|
-
if (decision.type === 'stop') {
|
|
5908
|
-
display.showSystemMessage(decision.message);
|
|
5909
|
-
break;
|
|
5910
|
-
}
|
|
5911
|
-
if (decision.type === 'stagnation-stop') {
|
|
5912
|
-
display.showWarning(decision.message);
|
|
5934
|
+
// Start streaming - no header needed, the input area already provides context
|
|
5935
|
+
this.startStreamingHeartbeat('Streaming response');
|
|
5936
|
+
if (orchestrate) {
|
|
5937
|
+
const orchestrator = new AgentOrchestrator(agent);
|
|
5938
|
+
orchestratorResult = await orchestrator.runToCompletion(request, {
|
|
5939
|
+
streaming: true,
|
|
5940
|
+
maxPasses: orchestratorPassLimit,
|
|
5941
|
+
maxStagnantPasses: 3,
|
|
5942
|
+
verificationMode: 'auto',
|
|
5943
|
+
enforceActions: true,
|
|
5944
|
+
});
|
|
5945
|
+
const MAX_CONTINUATIONS = 2;
|
|
5946
|
+
let continuationRuns = 0;
|
|
5947
|
+
while (orchestratorResult && continuationRuns < MAX_CONTINUATIONS) {
|
|
5948
|
+
const continuationDecision = this.shouldContinueOrchestrating(request, orchestratorResult);
|
|
5949
|
+
if (!continuationDecision.shouldContinue) {
|
|
5913
5950
|
break;
|
|
5914
5951
|
}
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
currentPrompt = applyInformationalGuardrail(decision.prompt);
|
|
5932
|
-
}
|
|
5933
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
5952
|
+
const reasonMessage = this.describeContinuationReason(continuationDecision.reason);
|
|
5953
|
+
display.showSystemMessage(`🔁 ${reasonMessage}`);
|
|
5954
|
+
const continuationPrompt = this.buildForcedContinuationPrompt(request, orchestratorResult, continuationDecision.reason ?? undefined);
|
|
5955
|
+
const continuationResult = await orchestrator.runToCompletion(continuationPrompt, {
|
|
5956
|
+
streaming: true,
|
|
5957
|
+
maxPasses: Math.max(orchestratorPassLimit, 12),
|
|
5958
|
+
maxStagnantPasses: 3,
|
|
5959
|
+
verificationMode: 'auto',
|
|
5960
|
+
enforceActions: true,
|
|
5961
|
+
});
|
|
5962
|
+
orchestratorResult = {
|
|
5963
|
+
finalResponse: continuationResult.finalResponse,
|
|
5964
|
+
passes: [...orchestratorResult.passes, ...continuationResult.passes],
|
|
5965
|
+
exitReason: continuationResult.exitReason,
|
|
5966
|
+
};
|
|
5967
|
+
continuationRuns++;
|
|
5934
5968
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5969
|
+
responseText = orchestratorResult.finalResponse;
|
|
5970
|
+
}
|
|
5971
|
+
else {
|
|
5972
|
+
responseText = await agent.send(request, true);
|
|
5973
|
+
}
|
|
5974
|
+
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5975
|
+
await this.awaitPendingCleanup();
|
|
5976
|
+
this.captureHistorySnapshot();
|
|
5977
|
+
this.autosaveIfEnabled();
|
|
5978
|
+
// Track metrics with Alpha Zero 2
|
|
5979
|
+
const elapsedMs = Date.now() - requestStartTime;
|
|
5980
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
5981
|
+
if (!responseText?.trim()) {
|
|
5982
|
+
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
5983
|
+
}
|
|
5984
|
+
// AlphaZero: Extract and track tool calls from response
|
|
5985
|
+
const toolsUsed = orchestratorResult
|
|
5986
|
+
? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
|
|
5987
|
+
: this.getExecutedTools(responseText);
|
|
5988
|
+
this.currentToolCalls = toolsUsed.map(name => ({
|
|
5989
|
+
name,
|
|
5990
|
+
arguments: {},
|
|
5991
|
+
success: true, // Assume success if we got here
|
|
5992
|
+
duration: 0,
|
|
5993
|
+
}));
|
|
5994
|
+
if (orchestratorResult && orchestratorResult.exitReason !== 'complete') {
|
|
5995
|
+
const exitDetail = (() => {
|
|
5996
|
+
switch (orchestratorResult?.exitReason) {
|
|
5997
|
+
case 'max-passes':
|
|
5998
|
+
return 'Reached orchestrator pass limit; showing last response.';
|
|
5999
|
+
case 'empty-response':
|
|
6000
|
+
return 'Received empty replies while orchestrating; showing last response.';
|
|
6001
|
+
case 'stalled':
|
|
6002
|
+
return 'Orchestrator detected stagnation; showing last response.';
|
|
6003
|
+
default:
|
|
6004
|
+
return null;
|
|
5945
6005
|
}
|
|
6006
|
+
})();
|
|
6007
|
+
if (exitDetail) {
|
|
6008
|
+
display.showSystemMessage(`⚠️ ${exitDetail}`);
|
|
5946
6009
|
}
|
|
5947
6010
|
}
|
|
5948
|
-
|
|
5949
|
-
|
|
6011
|
+
// AlphaZero: Check for failure in response
|
|
6012
|
+
const failure = detectFailure(responseText, {
|
|
6013
|
+
toolCalls: this.currentToolCalls,
|
|
6014
|
+
userMessage: request,
|
|
6015
|
+
});
|
|
6016
|
+
if (failure) {
|
|
6017
|
+
this.lastFailure = failure;
|
|
6018
|
+
// Check if we have a recovery strategy
|
|
6019
|
+
const strategy = findRecoveryStrategy(failure);
|
|
6020
|
+
if (strategy) {
|
|
6021
|
+
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
else {
|
|
6025
|
+
// Success - record the tool pattern for this task type
|
|
6026
|
+
if (this.currentToolCalls.length > 0) {
|
|
6027
|
+
const toolPattern = {
|
|
6028
|
+
taskType: this.currentTaskType,
|
|
6029
|
+
toolSequence: this.currentToolCalls.map(t => t.name),
|
|
6030
|
+
successRate: 1.0,
|
|
6031
|
+
avgDuration: elapsedMs,
|
|
6032
|
+
occurrences: 1,
|
|
6033
|
+
};
|
|
6034
|
+
addToolPattern(this.currentTaskType, toolPattern);
|
|
6035
|
+
}
|
|
6036
|
+
// Clear action history on success
|
|
6037
|
+
clearActionHistory();
|
|
6038
|
+
this.lastFailure = null;
|
|
5950
6039
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
6040
|
+
}
|
|
6041
|
+
catch (error) {
|
|
6042
|
+
const handled = this.handleProviderError(error, () => this.processRequest(request, options));
|
|
6043
|
+
if (!handled) {
|
|
6044
|
+
// Pass full error object for enhanced formatting with stack trace
|
|
6045
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
finally {
|
|
6049
|
+
// Fallback: if no assistant message was rendered (e.g., streaming hiccup), show the full response
|
|
6050
|
+
if (!this.responseRendered && responseText.trim()) {
|
|
6051
|
+
const finalText = responseText.trim();
|
|
5953
6052
|
display.showAssistantMessage(finalText, { isFinal: true });
|
|
5954
6053
|
this.ui.controller.recordAssistantResponse(finalText, {
|
|
5955
6054
|
source: 'final',
|
|
5956
6055
|
});
|
|
5957
6056
|
this.responseRendered = true;
|
|
5958
6057
|
}
|
|
5959
|
-
result = {
|
|
5960
|
-
finalResponse: lastResponseText,
|
|
5961
|
-
toolsUsed: lastToolsUsed,
|
|
5962
|
-
iterations: iteration,
|
|
5963
|
-
elapsedMs: Date.now() - overallStartTime,
|
|
5964
|
-
};
|
|
5965
|
-
}
|
|
5966
|
-
finally {
|
|
5967
6058
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5968
|
-
const totalElapsed = Date.now() - overallStartTime;
|
|
5969
|
-
const minutes = Math.floor(totalElapsed / 60000);
|
|
5970
|
-
const seconds = Math.floor((totalElapsed % 60000) / 1000);
|
|
5971
|
-
const completionLabel = mode === 'continuous'
|
|
5972
|
-
? `\n🏁 Continuous execution completed: ${iteration} iterations, ${minutes}m ${seconds}s total`
|
|
5973
|
-
: `\n🏁 Task run completed: ${iteration} iterations, ${minutes}m ${seconds}s total`;
|
|
5974
|
-
display.showSystemMessage(completionLabel);
|
|
5975
|
-
resetTaskCompletionDetector();
|
|
5976
6059
|
display.stopThinking(false);
|
|
5977
6060
|
this.uiUpdates.setMode('processing');
|
|
5978
6061
|
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
@@ -5985,54 +6068,13 @@ The previous reply was empty. Resume the task now: take the next action, call th
|
|
|
5985
6068
|
this.updateStatusMessage(null);
|
|
5986
6069
|
this.toolsUsedThisRun = [];
|
|
5987
6070
|
queueMicrotask(() => this.uiUpdates.setMode('idle'));
|
|
6071
|
+
// CRITICAL: Ensure readline prompt is active for user input
|
|
6072
|
+
// Erosolar-CLI style: New prompt naturally appears at bottom
|
|
5988
6073
|
this.ensureReadlineReady();
|
|
5989
6074
|
this.scheduleQueueProcessing();
|
|
5990
6075
|
this.maybeProcessPromptInbox();
|
|
5991
6076
|
this.refreshQueueIndicators();
|
|
5992
6077
|
}
|
|
5993
|
-
return result;
|
|
5994
|
-
}
|
|
5995
|
-
handleFlowRunOutcome(request, result) {
|
|
5996
|
-
this.currentToolCalls = result.toolsUsed.map((name) => ({
|
|
5997
|
-
name,
|
|
5998
|
-
arguments: {},
|
|
5999
|
-
success: true,
|
|
6000
|
-
duration: 0,
|
|
6001
|
-
}));
|
|
6002
|
-
const failure = detectFailure(result.finalResponse, {
|
|
6003
|
-
toolCalls: this.currentToolCalls,
|
|
6004
|
-
userMessage: request,
|
|
6005
|
-
});
|
|
6006
|
-
if (failure) {
|
|
6007
|
-
this.lastFailure = failure;
|
|
6008
|
-
const strategy = findRecoveryStrategy(failure);
|
|
6009
|
-
if (strategy) {
|
|
6010
|
-
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
6011
|
-
}
|
|
6012
|
-
return;
|
|
6013
|
-
}
|
|
6014
|
-
if (this.currentToolCalls.length > 0) {
|
|
6015
|
-
const toolPattern = {
|
|
6016
|
-
taskType: this.currentTaskType,
|
|
6017
|
-
toolSequence: this.currentToolCalls.map((t) => t.name),
|
|
6018
|
-
successRate: 1.0,
|
|
6019
|
-
avgDuration: result.elapsedMs,
|
|
6020
|
-
occurrences: 1,
|
|
6021
|
-
};
|
|
6022
|
-
addToolPattern(this.currentTaskType, toolPattern);
|
|
6023
|
-
}
|
|
6024
|
-
clearActionHistory();
|
|
6025
|
-
this.lastFailure = null;
|
|
6026
|
-
}
|
|
6027
|
-
async processRequest(request) {
|
|
6028
|
-
const result = await this.runFlowControlledTask(request, {
|
|
6029
|
-
maxIterations: 12,
|
|
6030
|
-
mode: 'standard',
|
|
6031
|
-
});
|
|
6032
|
-
if (!result) {
|
|
6033
|
-
return;
|
|
6034
|
-
}
|
|
6035
|
-
this.handleFlowRunOutcome(request, result);
|
|
6036
6078
|
}
|
|
6037
6079
|
/**
|
|
6038
6080
|
* Process a continuous/infinite loop request.
|
|
@@ -6047,14 +6089,29 @@ The previous reply was empty. Resume the task now: take the next action, call th
|
|
|
6047
6089
|
* Context is automatically managed - overflow errors trigger auto-recovery.
|
|
6048
6090
|
*/
|
|
6049
6091
|
async processContinuousRequest(initialRequest) {
|
|
6050
|
-
const
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
});
|
|
6054
|
-
if (!result) {
|
|
6092
|
+
const MAX_PASSES = 100; // Safety limit to prevent truly infinite loops
|
|
6093
|
+
if (this.isProcessing) {
|
|
6094
|
+
this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
|
|
6055
6095
|
return;
|
|
6056
6096
|
}
|
|
6057
|
-
this.
|
|
6097
|
+
if (!this.agent && !this.rebuildAgent()) {
|
|
6098
|
+
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
|
|
6102
|
+
const preparedRequest = this.isSelfImprovementRequest(initialRequest)
|
|
6103
|
+
? `${initialRequest}
|
|
6104
|
+
|
|
6105
|
+
IMPORTANT: You have full git access. After making improvements:
|
|
6106
|
+
1. Use bash to run: git status (see changes)
|
|
6107
|
+
2. Use bash to run: git add -A (stage changes)
|
|
6108
|
+
3. Use bash to run: git commit -m "descriptive message" (commit)
|
|
6109
|
+
4. Use bash to run: git push (when milestone reached)
|
|
6110
|
+
|
|
6111
|
+
Commit frequently with descriptive messages. Push when ready.
|
|
6112
|
+
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
6113
|
+
: initialRequest;
|
|
6114
|
+
await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
|
|
6058
6115
|
}
|
|
6059
6116
|
/**
|
|
6060
6117
|
* Resolve executed tools for the current turn. Prefer the actual tool
|
|
@@ -6076,6 +6133,129 @@ The previous reply was empty. Resume the task now: take the next action, call th
|
|
|
6076
6133
|
}
|
|
6077
6134
|
return this.extractToolsFromResponse(responseText);
|
|
6078
6135
|
}
|
|
6136
|
+
/**
|
|
6137
|
+
* Detect plan-only responses that narrate intent without executing actions.
|
|
6138
|
+
*/
|
|
6139
|
+
isPlanOnlyResponse(response) {
|
|
6140
|
+
const normalized = response.trim().toLowerCase();
|
|
6141
|
+
if (!normalized) {
|
|
6142
|
+
return false;
|
|
6143
|
+
}
|
|
6144
|
+
// If the assistant is clearly declaring completion, don't treat it as plan-only
|
|
6145
|
+
const completionGuards = [
|
|
6146
|
+
/\bnothing\s+(left|else)\s+(to\s+do|pending)\b/i,
|
|
6147
|
+
/\b(already|now)\s+(clean|complete|done)\b/i,
|
|
6148
|
+
/\b(no\s+(junk|issues?|changes?)\s+found)\b/i,
|
|
6149
|
+
];
|
|
6150
|
+
if (completionGuards.some((pattern) => pattern.test(response))) {
|
|
6151
|
+
return false;
|
|
6152
|
+
}
|
|
6153
|
+
const planIndicators = [
|
|
6154
|
+
/\bplan\b/i,
|
|
6155
|
+
/\bapproach\b/i,
|
|
6156
|
+
/\bsteps?:\b/i,
|
|
6157
|
+
/\bstep\s+1\b/i,
|
|
6158
|
+
/\bstart by\b/i,
|
|
6159
|
+
/\bfirst[, ]/i,
|
|
6160
|
+
/\bthen\b/i,
|
|
6161
|
+
/\bnext\b/i,
|
|
6162
|
+
/\bi['’]?\s*will\b/i,
|
|
6163
|
+
/\bi['’]?\s*ll\b/i,
|
|
6164
|
+
/\bi['’]?\s*can\b.{0,40}\bthen\b/i,
|
|
6165
|
+
/\bi['’]?\s*(?:will|ll)\s+begin\b/i,
|
|
6166
|
+
];
|
|
6167
|
+
return planIndicators.some((pattern) => pattern.test(response));
|
|
6168
|
+
}
|
|
6169
|
+
/**
|
|
6170
|
+
* Check if a response contains indicators that work is actually incomplete,
|
|
6171
|
+
* even if it also contains TASK_FULLY_COMPLETE marker.
|
|
6172
|
+
* This catches contradictory responses where the AI says "done" but also "not integrated yet".
|
|
6173
|
+
*/
|
|
6174
|
+
responseIndicatesIncompleteWork(response) {
|
|
6175
|
+
// Patterns that indicate work isn't actually complete
|
|
6176
|
+
// Organized by category for maintainability
|
|
6177
|
+
const incompletePatterns = [
|
|
6178
|
+
// === INTEGRATION/DEPLOYMENT STATE ===
|
|
6179
|
+
// "hasn't been integrated/implemented/connected yet"
|
|
6180
|
+
/hasn'?t\s+been\s+(integrated|implemented|connected|deployed|added|completed|tested|verified)\s*(yet|still)?/i,
|
|
6181
|
+
// "not yet integrated/implemented" or "not integrated"
|
|
6182
|
+
/not\s+(yet\s+)?(integrated|implemented|connected|deployed|functional|working|complete|tested|verified)/i,
|
|
6183
|
+
// "ready for integration" = NOT integrated
|
|
6184
|
+
/ready\s+(for|to\s+be)\s+(integration|integrated|connected|deployed|testing|review)/i,
|
|
6185
|
+
// "needs to be integrated"
|
|
6186
|
+
/needs?\s+to\s+be\s+(integrated|connected|deployed|added|hooked|wired|tested|reviewed|merged)/i,
|
|
6187
|
+
// Passive voice: "was not performed/completed"
|
|
6188
|
+
/was\s+not\s+(performed|completed|implemented|deployed|integrated|tested)/i,
|
|
6189
|
+
// "the [X] service hasn't been"
|
|
6190
|
+
/the\s+\w+\s+(service|module|component|feature)\s+hasn'?t\s+been/i,
|
|
6191
|
+
// === PARTIAL/INCOMPLETE STATE ===
|
|
6192
|
+
// "still stores/uses/has" (current bad state persists)
|
|
6193
|
+
/still\s+(stores?|uses?|has|contains?|needs?|requires?|missing|lacks?|broken)/i,
|
|
6194
|
+
// Partial completion: "partially", "mostly", "almost"
|
|
6195
|
+
/\b(partially|mostly|almost|nearly|not\s+fully)\s+(complete|done|finished|implemented|working)/i,
|
|
6196
|
+
// Explicit partial: "part of", "some of", "half of"
|
|
6197
|
+
/\b(only\s+)?(part|some|half|portion)\s+of\s+(the\s+)?(task|work|feature|implementation)/i,
|
|
6198
|
+
// === QUALIFIER WORDS (uncertain completion) ===
|
|
6199
|
+
// "should be complete", "appears complete", "theoretically"
|
|
6200
|
+
/\b(should|might|may|could|appears?\s+to)\s+be\s+(complete|done|working|functional)/i,
|
|
6201
|
+
/\btheoretically\s+(complete|done|working|functional)/i,
|
|
6202
|
+
// "assuming", "if everything works"
|
|
6203
|
+
/\b(assuming|provided|if)\s+(everything|it|this|that)\s+(works?|is\s+correct)/i,
|
|
6204
|
+
// === SELF-CONTRADICTION PHRASES ===
|
|
6205
|
+
// "done but...", "complete except...", "finished however..."
|
|
6206
|
+
/\b(done|complete|finished)\s+(but|except|however|although|though)/i,
|
|
6207
|
+
// "however" followed by incomplete indicator
|
|
6208
|
+
/however[,\s].{0,50}?(hasn'?t|not\s+yet|still\s+needs?|pending|remains?|missing|broken|failing)/i,
|
|
6209
|
+
// "but" followed by negative state
|
|
6210
|
+
/\bbut\s+.{0,30}?(not|hasn'?t|won'?t|can'?t|doesn'?t|isn'?t|wasn'?t)/i,
|
|
6211
|
+
// === FUTURE TENSE / DEFERRED WORK ===
|
|
6212
|
+
// "will need to", "will require"
|
|
6213
|
+
/will\s+(need\s+to|require|have\s+to)\s+(integrate|connect|deploy|complete|implement|test|fix)/i,
|
|
6214
|
+
// Deferred: "left as", "deferred", "postponed", "out of scope"
|
|
6215
|
+
/\b(left\s+as|deferred|postponed|out\s+of\s+scope|for\s+later|in\s+a\s+future)/i,
|
|
6216
|
+
// Time-dependent: "after restart", "takes effect after", "once you"
|
|
6217
|
+
/\b(after\s+(restart|reboot|redeploy)|takes?\s+effect\s+after|once\s+you)/i,
|
|
6218
|
+
// === REMAINING WORK INDICATORS ===
|
|
6219
|
+
// "remaining tasks", "outstanding items"
|
|
6220
|
+
/\b(remaining|outstanding|pending|leftover)\s+(tasks?|items?|work|issues?|steps?)/i,
|
|
6221
|
+
// "X more to do", "still have to"
|
|
6222
|
+
/\b(more\s+to\s+do|still\s+have\s+to|yet\s+to\s+be\s+done)/i,
|
|
6223
|
+
// Explicit blockers
|
|
6224
|
+
/\b(blocker|blocked\s+by|waiting\s+(for|on)|depends?\s+on)/i,
|
|
6225
|
+
// === ERROR/FAILURE STATE ===
|
|
6226
|
+
// "failing tests", "build errors"
|
|
6227
|
+
/\b(failing|broken|erroring)\s+(tests?|builds?|checks?|validations?)/i,
|
|
6228
|
+
// "tests? (are )?(still )?failing"
|
|
6229
|
+
/\btests?\s+(are\s+)?(still\s+)?failing/i,
|
|
6230
|
+
// "errors? to (address|fix)"
|
|
6231
|
+
/\b(errors?|warnings?|issues?)\s+to\s+(address|fix|resolve)/i,
|
|
6232
|
+
// "doesn't work", "isn't working", "not working"
|
|
6233
|
+
/\b(doesn'?t|isn'?t|not)\s+(work|working|functional|functioning)/i,
|
|
6234
|
+
// === MANUAL STEPS REQUIRED ===
|
|
6235
|
+
// "you'll need to", "manually run", "requires user"
|
|
6236
|
+
/\b(you('ll|\s+will)\s+need\s+to|manually\s+(run|configure|set|update)|requires?\s+user)/i,
|
|
6237
|
+
// "run this command", "execute the following"
|
|
6238
|
+
/\b(run\s+this|execute\s+the\s+following|apply\s+the\s+migration)/i,
|
|
6239
|
+
// === TODO/FIXME IN PROSE ===
|
|
6240
|
+
// TODO or FIXME mentioned as remaining work (not in code blocks)
|
|
6241
|
+
/\b(todo|fixme|hack|xxx):\s/i,
|
|
6242
|
+
// "need to add", "should implement"
|
|
6243
|
+
/\b(need\s+to|should|must)\s+(add|implement|create|write|build|fix)\b/i,
|
|
6244
|
+
// === SCOPE LIMITATIONS ===
|
|
6245
|
+
// "didn't have time", "ran out of time"
|
|
6246
|
+
/\b(didn'?t|did\s+not)\s+have\s+(time|chance|opportunity)/i,
|
|
6247
|
+
// "beyond scope", "outside scope"
|
|
6248
|
+
/\b(beyond|outside)\s+(the\s+)?scope/i,
|
|
6249
|
+
// "for now" (temporary state)
|
|
6250
|
+
/\b(for\s+now|at\s+this\s+point|currently)\s*.{0,20}?(not|without|lacks?|missing)/i,
|
|
6251
|
+
];
|
|
6252
|
+
for (const pattern of incompletePatterns) {
|
|
6253
|
+
if (pattern.test(response)) {
|
|
6254
|
+
return true;
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
return false;
|
|
6258
|
+
}
|
|
6079
6259
|
/**
|
|
6080
6260
|
* Extract tool names from a response by looking for tool call patterns
|
|
6081
6261
|
*/
|
|
@@ -6568,19 +6748,12 @@ Return ONLY JSON array:
|
|
|
6568
6748
|
const activity = normalized ? `Working: ${normalized}` : 'Working';
|
|
6569
6749
|
this.renderer?.setActivity(activity);
|
|
6570
6750
|
},
|
|
6571
|
-
onBeforeFirstToolCall: (toolNames
|
|
6751
|
+
onBeforeFirstToolCall: (toolNames) => {
|
|
6572
6752
|
const primaryTool = toolNames[0];
|
|
6573
6753
|
if (primaryTool) {
|
|
6574
6754
|
this.renderer?.setActivity(`Running ${primaryTool}`);
|
|
6575
6755
|
}
|
|
6576
|
-
|
|
6577
|
-
return undefined;
|
|
6578
|
-
}
|
|
6579
|
-
const toolList = toolNames.length ? toolNames.join(', ') : 'tools';
|
|
6580
|
-
const ack = `💭 Plan before tools:\n- Goal: ${this.lastUserQuery || 'address request'}\n- Tools: ${toolList}\n- Executing now.`;
|
|
6581
|
-
display.showNarrative(ack);
|
|
6582
|
-
this.ui.controller.recordAssistantThought(ack);
|
|
6583
|
-
return ack;
|
|
6756
|
+
return undefined;
|
|
6584
6757
|
},
|
|
6585
6758
|
onStreamChunk: (chunk, type) => {
|
|
6586
6759
|
this.handleStreamChunk(chunk, type ?? 'content');
|