erosolar-cli 2.1.193 → 2.1.195
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/capabilities/offsecOpsCapability.d.ts +2 -2
- package/dist/capabilities/offsecOpsCapability.d.ts.map +1 -1
- package/dist/capabilities/offsecOpsCapability.js +6 -7
- package/dist/capabilities/offsecOpsCapability.js.map +1 -1
- package/dist/contracts/models.schema.json +9 -0
- package/dist/core/agent.d.ts +31 -12
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +71 -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/alphaZeroOrchestrator.d.ts +140 -0
- package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -0
- package/dist/core/alphaZeroOrchestrator.js +418 -0
- package/dist/core/alphaZeroOrchestrator.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 +106 -0
- package/dist/core/taskCompletionDetector.d.ts.map +1 -0
- package/dist/core/taskCompletionDetector.js +402 -0
- 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/core/types.d.ts +14 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/providers/anthropicProvider.d.ts +8 -1
- package/dist/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropicProvider.js +51 -0
- package/dist/providers/anthropicProvider.js.map +1 -1
- package/dist/providers/googleProvider.d.ts +7 -1
- package/dist/providers/googleProvider.d.ts.map +1 -1
- package/dist/providers/googleProvider.js +41 -0
- package/dist/providers/googleProvider.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts +7 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +44 -0
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +28 -4
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +478 -191
- 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 -46
- package/dist/runtime/flowOrchestrator.d.ts.map +0 -1
- package/dist/runtime/flowOrchestrator.js +0 -80
- package/dist/runtime/flowOrchestrator.js.map +0 -1
- package/dist/shell/taskCompletionDetector.d.ts +0 -52
- package/dist/shell/taskCompletionDetector.d.ts.map +0 -1
- package/dist/shell/taskCompletionDetector.js +0 -131
- package/dist/shell/taskCompletionDetector.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,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';
|
|
@@ -37,8 +36,8 @@ import { analyzeImprovementOpportunities, runSelfImprovementCycle, getImprovemen
|
|
|
37
36
|
import { listAvailablePlugins } from '../plugins/index.js';
|
|
38
37
|
import { isValidSourceRepo, getRepoName, analyzeSource, runSelfEvolution, stopEvolution, getEvolutionStatus, emergencyEvolutionRollback, learnSourcePatterns, generateFix, } from '../core/selfEvolution.js';
|
|
39
38
|
import { analyzeTokenUsage, discoverModularTargets, getModularStatusDisplay, generateContextOptimizations, getGuidelines, deleteGuideline, getPendingActions, executeModularAction, } from '../core/alphaZeroModular.js';
|
|
40
|
-
import { startOffsecRun, resumeOffsecRun, recordOffsecOutcome, getOffsecNextActions, simulateOffsecRollout, formatOffsecStatus, listOffsecRuns, } from '../core/offsecAlphaZero.js';
|
|
41
39
|
import { generateTestFlows, detectBugs, detectUIUpdates, saveTestFlows, saveBugReports, saveUIUpdates, getTestFlowStatus, } from '../core/intelligentTestFlows.js';
|
|
40
|
+
import { startOffsecRun, resumeOffsecRun, listOffsecRuns, getOffsecNextActions, simulateOffsecRollout, recordOffsecOutcome, formatOffsecStatus, } from '../core/offsecAlphaZero.js';
|
|
42
41
|
import { PromptController } from '../ui/PromptController.js';
|
|
43
42
|
import { enterStreamingMode, exitStreamingMode, isStreamingMode } from '../ui/globalWriteLock.js';
|
|
44
43
|
import { setGlobalAIEnhancer } from '../tools/localExplore.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';
|
|
@@ -1629,7 +1624,10 @@ export class InteractiveShell {
|
|
|
1629
1624
|
return `${provider} · ${this.sessionState.model}`;
|
|
1630
1625
|
}
|
|
1631
1626
|
refreshContextGauge() {
|
|
1632
|
-
|
|
1627
|
+
// First try to get context window from provider API (real value)
|
|
1628
|
+
// Fall back to static model mapping only if provider API is not available
|
|
1629
|
+
const providerContextWindow = this.agent?.getModelInfo()?.contextWindow;
|
|
1630
|
+
const tokens = providerContextWindow ?? getContextWindowTokens(this.sessionState.model);
|
|
1633
1631
|
const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
|
|
1634
1632
|
this.activeContextWindowTokens = normalizedTokens;
|
|
1635
1633
|
if (normalizedTokens !== null) {
|
|
@@ -1639,6 +1637,28 @@ export class InteractiveShell {
|
|
|
1639
1637
|
};
|
|
1640
1638
|
}
|
|
1641
1639
|
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Fetch real context window from provider API and update the gauge.
|
|
1642
|
+
* This should be called after the agent is created.
|
|
1643
|
+
*/
|
|
1644
|
+
async fetchAndUpdateContextWindow() {
|
|
1645
|
+
if (!this.agent) {
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
try {
|
|
1649
|
+
const contextWindow = await this.agent.getContextWindowFromProvider();
|
|
1650
|
+
if (contextWindow !== null && Number.isFinite(contextWindow)) {
|
|
1651
|
+
this.activeContextWindowTokens = contextWindow;
|
|
1652
|
+
this.latestTokenUsage = {
|
|
1653
|
+
used: this.latestTokenUsage.used,
|
|
1654
|
+
limit: contextWindow,
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
catch {
|
|
1659
|
+
// Ignore errors - keep using static mapping
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1642
1662
|
updateContextUsage(percentage, autoCompactThreshold = CONTEXT_AUTOCOMPACT_PERCENT) {
|
|
1643
1663
|
this.uiAdapter.updateContextUsage(percentage);
|
|
1644
1664
|
this.terminalInput.setContextUsage(percentage);
|
|
@@ -1730,6 +1750,26 @@ export class InteractiveShell {
|
|
|
1730
1750
|
}
|
|
1731
1751
|
return Math.max(0, Math.floor(totalMs / 1000));
|
|
1732
1752
|
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Format elapsed time in human-readable format (e.g., "2m 34s" or "1h 5m 12s")
|
|
1755
|
+
*/
|
|
1756
|
+
formatElapsedTime(seconds) {
|
|
1757
|
+
if (seconds === null || seconds <= 0) {
|
|
1758
|
+
return '0s';
|
|
1759
|
+
}
|
|
1760
|
+
const hours = Math.floor(seconds / 3600);
|
|
1761
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
1762
|
+
const secs = seconds % 60;
|
|
1763
|
+
const parts = [];
|
|
1764
|
+
if (hours > 0) {
|
|
1765
|
+
parts.push(`${hours}h`);
|
|
1766
|
+
}
|
|
1767
|
+
if (minutes > 0 || hours > 0) {
|
|
1768
|
+
parts.push(`${minutes}m`);
|
|
1769
|
+
}
|
|
1770
|
+
parts.push(`${secs}s`);
|
|
1771
|
+
return parts.join(' ');
|
|
1772
|
+
}
|
|
1733
1773
|
/**
|
|
1734
1774
|
* Refresh the status line in the persistent input area.
|
|
1735
1775
|
* Uses combined status display: streaming label + override + main status.
|
|
@@ -1997,9 +2037,8 @@ export class InteractiveShell {
|
|
|
1997
2037
|
return;
|
|
1998
2038
|
}
|
|
1999
2039
|
const isReasoning = type === 'reasoning';
|
|
2000
|
-
//
|
|
2001
|
-
|
|
2002
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
2040
|
+
// Token count is updated from real provider usage data in onAssistantMessage
|
|
2041
|
+
// Do NOT estimate tokens from chunk length - wait for actual API response
|
|
2003
2042
|
// Keep pinned status updated for all streaming chunks
|
|
2004
2043
|
this.updateStreamingStatusFromChunk(chunk);
|
|
2005
2044
|
// Handle <thinking> tags as separate events in the queue
|
|
@@ -2276,14 +2315,17 @@ export class InteractiveShell {
|
|
|
2276
2315
|
// Check for continuous/infinite loop commands or auto-escalation to completion mode
|
|
2277
2316
|
const explicitContinuous = this.isContinuousCommand(trimmed);
|
|
2278
2317
|
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
|
-
}
|
|
2318
|
+
if (explicitContinuous) {
|
|
2283
2319
|
await this.processContinuousRequest(trimmed);
|
|
2284
2320
|
this.syncRendererInput();
|
|
2285
2321
|
return;
|
|
2286
2322
|
}
|
|
2323
|
+
if (autoContinuous) {
|
|
2324
|
+
display.showSystemMessage('⚡ Actionable request detected; orchestrating until complete (Ctrl+C to stop).');
|
|
2325
|
+
await this.processRequest(trimmed, { orchestrate: true });
|
|
2326
|
+
this.syncRendererInput();
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2287
2329
|
// Direct execution for all inputs, including multi-line pastes
|
|
2288
2330
|
await this.processRequest(trimmed);
|
|
2289
2331
|
this.syncRendererInput();
|
|
@@ -2320,19 +2362,113 @@ export class InteractiveShell {
|
|
|
2320
2362
|
];
|
|
2321
2363
|
const strongMaintenanceIntent = maintenancePatterns.some((pattern) => pattern.test(normalized));
|
|
2322
2364
|
// 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/;
|
|
2365
|
+
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
2366
|
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
|
-
|
|
2367
|
+
const hasActionVerb = actionVerb.test(normalized);
|
|
2368
|
+
const hasCodeContext = codeContext.test(normalized);
|
|
2369
|
+
// Bug/issue hunt or health-check intents, even when phrased as questions
|
|
2370
|
+
const bugSweepIntent = /\b(got\s+any|any|open|known)\s+(bugs?|issues?|defects?)\b/;
|
|
2371
|
+
const triageIntent = /\b(find|check|hunt|triage|detect|scan|review|look\s+for)\s+(bugs?|issues?|errors?|failures?)\b/;
|
|
2372
|
+
const failingTestsIntent = /\b(failing|broken|red)\s+tests?\b|\btests?\s+(are\s+)?failing\b/;
|
|
2373
|
+
const qualitySweepIntent = /\b(audit|health\s+check|stability\s+check|bug\s+scan|issue\s+scan|regression\s+pass)\b/;
|
|
2374
|
+
const bugOrHealthIntent = [bugSweepIntent, triageIntent, failingTestsIntent, qualitySweepIntent].some((pattern) => pattern.test(normalized));
|
|
2375
|
+
// Score-based intent detection keeps questions like "what is this repo?" out
|
|
2376
|
+
let actionScore = 0;
|
|
2377
|
+
if (strongMaintenanceIntent)
|
|
2378
|
+
actionScore += 2;
|
|
2379
|
+
if (bugOrHealthIntent)
|
|
2380
|
+
actionScore += 2;
|
|
2381
|
+
if (hasActionVerb)
|
|
2382
|
+
actionScore += 1;
|
|
2383
|
+
if (hasCodeContext)
|
|
2384
|
+
actionScore += 1;
|
|
2385
|
+
const decisiveAction = actionScore >= 2 || (actionScore === 1 && !isQuestion && normalized.length > 10);
|
|
2386
|
+
if (!decisiveAction) {
|
|
2387
|
+
return false;
|
|
2388
|
+
}
|
|
2389
|
+
// Respect informational openers unless intent is clearly actionable
|
|
2390
|
+
if (startsWithInfoWord && actionScore < 3 && !bugOrHealthIntent && !strongMaintenanceIntent) {
|
|
2391
|
+
return false;
|
|
2392
|
+
}
|
|
2393
|
+
return true;
|
|
2394
|
+
}
|
|
2395
|
+
shouldContinueOrchestrating(originalRequest, result) {
|
|
2396
|
+
if (!result) {
|
|
2397
|
+
return { shouldContinue: false, reason: null };
|
|
2398
|
+
}
|
|
2399
|
+
const actionable = this.shouldAutoRunToCompletion(originalRequest);
|
|
2400
|
+
if (!actionable) {
|
|
2401
|
+
const incomplete = result.exitReason !== 'complete';
|
|
2402
|
+
return { shouldContinue: incomplete, reason: incomplete ? result.exitReason : null };
|
|
2403
|
+
}
|
|
2404
|
+
if (result.exitReason !== 'complete') {
|
|
2405
|
+
return { shouldContinue: true, reason: result.exitReason };
|
|
2406
|
+
}
|
|
2407
|
+
const executedTools = result.passes.some(pass => (pass.toolsUsed?.length ?? 0) > 0);
|
|
2408
|
+
const lastPass = result.passes[result.passes.length - 1];
|
|
2409
|
+
const lastPlanOnly = Boolean(lastPass?.planOnly) && !lastPass?.tookAction && (!lastPass?.toolsUsed?.length);
|
|
2410
|
+
const lastEmpty = !lastPass?.response?.trim();
|
|
2411
|
+
if (!executedTools) {
|
|
2412
|
+
return { shouldContinue: true, reason: 'no-action' };
|
|
2413
|
+
}
|
|
2414
|
+
if (lastPlanOnly) {
|
|
2415
|
+
return { shouldContinue: true, reason: 'plan-only' };
|
|
2416
|
+
}
|
|
2417
|
+
if (lastEmpty) {
|
|
2418
|
+
return { shouldContinue: true, reason: 'empty-response' };
|
|
2419
|
+
}
|
|
2420
|
+
return { shouldContinue: false, reason: null };
|
|
2421
|
+
}
|
|
2422
|
+
describeContinuationReason(reason) {
|
|
2423
|
+
switch (reason) {
|
|
2424
|
+
case 'no-action':
|
|
2425
|
+
return 'No tools or concrete actions were executed; continuing until real work is done.';
|
|
2426
|
+
case 'plan-only':
|
|
2427
|
+
return 'Last pass was just planning; executing the next concrete step now.';
|
|
2428
|
+
case 'empty-response':
|
|
2429
|
+
case 'empty':
|
|
2430
|
+
return 'Previous pass returned nothing; resuming with a concrete action.';
|
|
2431
|
+
case 'stalled':
|
|
2432
|
+
return 'Previous orchestration stalled; forcing continuation.';
|
|
2433
|
+
case 'max-passes':
|
|
2434
|
+
return 'Reached pass limit; extending the run to finish the task.';
|
|
2435
|
+
default:
|
|
2436
|
+
return 'Continuing orchestration until the task is actually finished.';
|
|
2333
2437
|
}
|
|
2334
|
-
return false;
|
|
2335
2438
|
}
|
|
2439
|
+
buildForcedContinuationPrompt(originalRequest, lastResult, continuationReason) {
|
|
2440
|
+
const lastPass = lastResult.passes[lastResult.passes.length - 1];
|
|
2441
|
+
const rawResponse = lastPass?.response?.trim() ?? '';
|
|
2442
|
+
const truncatedResponse = rawResponse ? rawResponse.slice(0, 1200) : '';
|
|
2443
|
+
const responseNote = rawResponse && rawResponse.length > truncatedResponse.length
|
|
2444
|
+
? '\n\n[Last response truncated]'
|
|
2445
|
+
: '';
|
|
2446
|
+
const usedTools = lastResult.passes.flatMap(pass => pass.toolsUsed ?? []);
|
|
2447
|
+
const hasToolUsage = usedTools.length > 0;
|
|
2448
|
+
const toolsUsed = hasToolUsage
|
|
2449
|
+
? `Tools used so far: ${usedTools.join(', ')}.`
|
|
2450
|
+
: 'No tools have been used yet - start with a quick workspace scan (list_files + glob/grep/search) and then take concrete actions now.';
|
|
2451
|
+
const responseSection = truncatedResponse
|
|
2452
|
+
? `Last response snapshot:\n${truncatedResponse}${responseNote}\n`
|
|
2453
|
+
: '';
|
|
2454
|
+
const exitReason = continuationReason ?? lastResult.exitReason ?? 'incomplete';
|
|
2455
|
+
const reasonLine = this.describeContinuationReason(exitReason);
|
|
2456
|
+
const missingActionLine = hasToolUsage
|
|
2457
|
+
? ''
|
|
2458
|
+
: '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.';
|
|
2459
|
+
return `${originalRequest.trim()}
|
|
2460
|
+
|
|
2461
|
+
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.
|
|
2462
|
+
|
|
2463
|
+
${reasonLine}
|
|
2464
|
+
${toolsUsed}
|
|
2465
|
+
${responseSection}
|
|
2466
|
+
${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concrete action now.`;
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Decide whether to automatically continue execution when the model stops after a plan/summary
|
|
2470
|
+
* without taking actions. This keeps flows moving toward completion instead of stalling on planning.
|
|
2471
|
+
*/
|
|
2336
2472
|
isExitCommand(input) {
|
|
2337
2473
|
const lower = input.trim().toLowerCase();
|
|
2338
2474
|
return (lower === 'exit' ||
|
|
@@ -5780,22 +5916,20 @@ export class InteractiveShell {
|
|
|
5780
5916
|
this.clearInlinePanel();
|
|
5781
5917
|
this.syncRendererInput();
|
|
5782
5918
|
}
|
|
5783
|
-
async
|
|
5784
|
-
const { mode } = options;
|
|
5785
|
-
const followUpType = mode === 'continuous' ? 'continuous' : 'request';
|
|
5919
|
+
async processRequest(request, options) {
|
|
5786
5920
|
if (this.isProcessing) {
|
|
5787
|
-
this.enqueueFollowUpAction({ type:
|
|
5788
|
-
return
|
|
5921
|
+
this.enqueueFollowUpAction({ type: 'request', text: request });
|
|
5922
|
+
return;
|
|
5789
5923
|
}
|
|
5790
5924
|
if (!this.agent && !this.rebuildAgent()) {
|
|
5791
5925
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
5792
|
-
return
|
|
5926
|
+
return;
|
|
5793
5927
|
}
|
|
5794
5928
|
this.inlinePanelScopeActive = false;
|
|
5795
5929
|
this.clearInlinePanel();
|
|
5796
5930
|
const agent = this.agent;
|
|
5797
5931
|
if (!agent) {
|
|
5798
|
-
return
|
|
5932
|
+
return;
|
|
5799
5933
|
}
|
|
5800
5934
|
this.toolsUsedThisRun = [];
|
|
5801
5935
|
this.currentToolCalls = [];
|
|
@@ -5806,138 +5940,187 @@ export class InteractiveShell {
|
|
|
5806
5940
|
else {
|
|
5807
5941
|
this.resetNetworkRetryState();
|
|
5808
5942
|
}
|
|
5943
|
+
// Reset per-request render tracking
|
|
5809
5944
|
this.responseRendered = false;
|
|
5810
|
-
if (this.shouldLogPrompt(
|
|
5811
|
-
this.logUserPrompt(
|
|
5945
|
+
if (this.shouldLogPrompt(request)) {
|
|
5946
|
+
this.logUserPrompt(request);
|
|
5812
5947
|
}
|
|
5813
5948
|
this.isProcessing = true;
|
|
5814
5949
|
this.uiUpdates.setMode('processing');
|
|
5815
|
-
this.streamingTokenCount = 0;
|
|
5950
|
+
this.streamingTokenCount = 0; // Reset token counter for new request
|
|
5816
5951
|
this.terminalInput.setStreaming(true);
|
|
5952
|
+
// Keep the persistent input/control bar active as we transition into streaming.
|
|
5817
5953
|
this.syncRendererInput();
|
|
5818
5954
|
this.renderer?.render();
|
|
5819
|
-
const
|
|
5955
|
+
const requestStartTime = Date.now(); // Alpha Zero 2 timing
|
|
5956
|
+
// Clear previous parallel agents and start fresh for new request
|
|
5820
5957
|
const parallelManager = getParallelAgentManager();
|
|
5821
5958
|
parallelManager.clear();
|
|
5822
5959
|
parallelManager.startBatch();
|
|
5823
|
-
|
|
5824
|
-
this.
|
|
5960
|
+
// AlphaZero: Track task for learning
|
|
5961
|
+
this.lastUserQuery = request;
|
|
5962
|
+
this.currentTaskType = classifyTaskType(request);
|
|
5825
5963
|
this.currentToolCalls = [];
|
|
5826
5964
|
this.clearToolUsageMeta();
|
|
5827
5965
|
this.renderer?.setActivity('Starting...');
|
|
5828
|
-
this.uiAdapter.startProcessing(
|
|
5966
|
+
this.uiAdapter.startProcessing('Working on your request');
|
|
5829
5967
|
this.setProcessingStatus();
|
|
5830
5968
|
this.beginAiRuntime();
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
this.
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
}
|
|
5839
|
-
else {
|
|
5840
|
-
display.showSystemMessage('Flow orchestrator engaged; running until the request is satisfied.');
|
|
5841
|
-
}
|
|
5842
|
-
let currentPrompt = initialRequest;
|
|
5843
|
-
if (this.isSelfImprovementRequest(initialRequest)) {
|
|
5844
|
-
currentPrompt = `${initialRequest}
|
|
5845
|
-
|
|
5846
|
-
When finished with ALL tasks, say "TASK_FULLY_COMPLETE".`;
|
|
5969
|
+
let responseText = '';
|
|
5970
|
+
let orchestratorResult = null;
|
|
5971
|
+
const orchestrate = options?.orchestrate ?? true;
|
|
5972
|
+
const orchestratorPassLimit = options?.maxPasses ?? 10;
|
|
5973
|
+
if (orchestrate && !this.orchestrationNoticeShown) {
|
|
5974
|
+
display.showSystemMessage('⚡ Orchestrating every prompt until completion. Press Ctrl+C to stop a run early.');
|
|
5975
|
+
this.orchestrationNoticeShown = true;
|
|
5847
5976
|
}
|
|
5848
|
-
let iteration = 0;
|
|
5849
|
-
let lastResponseText = '';
|
|
5850
|
-
let lastToolsUsed = [];
|
|
5851
|
-
let result = null;
|
|
5852
5977
|
try {
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
this.
|
|
5868
|
-
if (!
|
|
5869
|
-
display.showWarning('Model returned an empty response. Continuing orchestration...');
|
|
5870
|
-
currentPrompt = `${initialRequest}
|
|
5871
|
-
|
|
5872
|
-
The previous reply was empty. Resume the task now.`;
|
|
5873
|
-
continue;
|
|
5874
|
-
}
|
|
5875
|
-
const toolsUsed = this.getExecutedTools(response);
|
|
5876
|
-
lastToolsUsed = toolsUsed;
|
|
5877
|
-
toolsUsed.forEach(tool => completionDetector.recordToolCall(tool, true, true));
|
|
5878
|
-
const completionAnalysis = completionDetector.analyzeCompletion(response, toolsUsed);
|
|
5879
|
-
const decision = this.flowOrchestrator.decide({
|
|
5880
|
-
iteration,
|
|
5881
|
-
response,
|
|
5882
|
-
toolsUsed,
|
|
5883
|
-
completionAnalysis,
|
|
5884
|
-
verificationConfirmed: this.flowOrchestrator.isVerificationPending()
|
|
5885
|
-
? completionDetector.isVerificationConfirmed(response)
|
|
5886
|
-
: false,
|
|
5887
|
-
});
|
|
5888
|
-
if (decision.type === 'stop') {
|
|
5889
|
-
display.showSystemMessage(decision.message);
|
|
5890
|
-
break;
|
|
5891
|
-
}
|
|
5892
|
-
if (decision.type === 'stagnation-stop') {
|
|
5893
|
-
display.showWarning(decision.message);
|
|
5978
|
+
// Start streaming - no header needed, the input area already provides context
|
|
5979
|
+
this.startStreamingHeartbeat('Streaming response');
|
|
5980
|
+
if (orchestrate) {
|
|
5981
|
+
const orchestrator = new AgentOrchestrator(agent);
|
|
5982
|
+
orchestratorResult = await orchestrator.runToCompletion(request, {
|
|
5983
|
+
streaming: true,
|
|
5984
|
+
maxPasses: orchestratorPassLimit,
|
|
5985
|
+
maxStagnantPasses: 3,
|
|
5986
|
+
verificationMode: 'auto',
|
|
5987
|
+
enforceActions: true,
|
|
5988
|
+
});
|
|
5989
|
+
const MAX_CONTINUATIONS = 2;
|
|
5990
|
+
let continuationRuns = 0;
|
|
5991
|
+
while (orchestratorResult && continuationRuns < MAX_CONTINUATIONS) {
|
|
5992
|
+
const continuationDecision = this.shouldContinueOrchestrating(request, orchestratorResult);
|
|
5993
|
+
if (!continuationDecision.shouldContinue) {
|
|
5894
5994
|
break;
|
|
5895
5995
|
}
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5996
|
+
const reasonMessage = this.describeContinuationReason(continuationDecision.reason);
|
|
5997
|
+
display.showSystemMessage(`🔁 ${reasonMessage}`);
|
|
5998
|
+
const continuationPrompt = this.buildForcedContinuationPrompt(request, orchestratorResult, continuationDecision.reason ?? undefined);
|
|
5999
|
+
const continuationResult = await orchestrator.runToCompletion(continuationPrompt, {
|
|
6000
|
+
streaming: true,
|
|
6001
|
+
maxPasses: Math.max(orchestratorPassLimit, 12),
|
|
6002
|
+
maxStagnantPasses: 3,
|
|
6003
|
+
verificationMode: 'auto',
|
|
6004
|
+
enforceActions: true,
|
|
6005
|
+
});
|
|
6006
|
+
orchestratorResult = {
|
|
6007
|
+
finalResponse: continuationResult.finalResponse,
|
|
6008
|
+
passes: [...orchestratorResult.passes, ...continuationResult.passes],
|
|
6009
|
+
exitReason: continuationResult.exitReason,
|
|
6010
|
+
};
|
|
6011
|
+
continuationRuns++;
|
|
5903
6012
|
}
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
6013
|
+
responseText = orchestratorResult.finalResponse;
|
|
6014
|
+
}
|
|
6015
|
+
else {
|
|
6016
|
+
responseText = await agent.send(request, true);
|
|
6017
|
+
}
|
|
6018
|
+
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
6019
|
+
await this.awaitPendingCleanup();
|
|
6020
|
+
this.captureHistorySnapshot();
|
|
6021
|
+
this.autosaveIfEnabled();
|
|
6022
|
+
// Track metrics with Alpha Zero 2
|
|
6023
|
+
const elapsedMs = Date.now() - requestStartTime;
|
|
6024
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
6025
|
+
if (!responseText?.trim()) {
|
|
6026
|
+
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
6027
|
+
}
|
|
6028
|
+
// AlphaZero: Extract and track tool calls from response
|
|
6029
|
+
const toolsUsed = orchestratorResult
|
|
6030
|
+
? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
|
|
6031
|
+
: this.getExecutedTools(responseText);
|
|
6032
|
+
this.currentToolCalls = toolsUsed.map(name => ({
|
|
6033
|
+
name,
|
|
6034
|
+
arguments: {},
|
|
6035
|
+
success: true, // Assume success if we got here
|
|
6036
|
+
duration: 0,
|
|
6037
|
+
}));
|
|
6038
|
+
if (orchestratorResult && orchestratorResult.exitReason !== 'complete') {
|
|
6039
|
+
const exitDetail = (() => {
|
|
6040
|
+
switch (orchestratorResult?.exitReason) {
|
|
6041
|
+
case 'max-passes':
|
|
6042
|
+
return 'Reached orchestrator pass limit; showing last response.';
|
|
6043
|
+
case 'empty-response':
|
|
6044
|
+
return 'Received empty replies while orchestrating; showing last response.';
|
|
6045
|
+
case 'stalled':
|
|
6046
|
+
return 'Orchestrator detected stagnation; showing last response.';
|
|
6047
|
+
default:
|
|
6048
|
+
return null;
|
|
5914
6049
|
}
|
|
6050
|
+
})();
|
|
6051
|
+
if (exitDetail) {
|
|
6052
|
+
display.showSystemMessage(`⚠️ ${exitDetail}`);
|
|
5915
6053
|
}
|
|
5916
6054
|
}
|
|
5917
|
-
if (
|
|
5918
|
-
|
|
6055
|
+
else if (orchestratorResult && orchestratorResult.exitReason === 'complete') {
|
|
6056
|
+
// Task fully complete - show emphasis with elapsed time
|
|
6057
|
+
const lastResponse = orchestratorResult.passes[orchestratorResult.passes.length - 1]?.response ?? '';
|
|
6058
|
+
const hasExplicitMarker = lastResponse.includes('TASK_FULLY_COMPLETE');
|
|
6059
|
+
if (hasExplicitMarker || orchestratorResult.passes.length > 0) {
|
|
6060
|
+
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
6061
|
+
const elapsedDisplay = this.formatElapsedTime(elapsedSeconds);
|
|
6062
|
+
const totalPasses = orchestratorResult.passes.length;
|
|
6063
|
+
const toolsUsed = orchestratorResult.passes.flatMap(p => p.toolsUsed ?? []).length;
|
|
6064
|
+
// Visual separator and completion banner
|
|
6065
|
+
display.showSystemMessage('');
|
|
6066
|
+
display.showSystemMessage(theme.gradient.success('━'.repeat(50)));
|
|
6067
|
+
display.showSystemMessage(theme.gradient.success(`✅ TASK FULLY COMPLETE`));
|
|
6068
|
+
display.showSystemMessage(theme.gradient.success('━'.repeat(50)));
|
|
6069
|
+
display.showSystemMessage(` ⏱️ Total time: ${elapsedDisplay}`);
|
|
6070
|
+
display.showSystemMessage(` 🔄 Passes: ${totalPasses} | 🛠️ Tool calls: ${toolsUsed}`);
|
|
6071
|
+
display.showSystemMessage('');
|
|
6072
|
+
// Update renderer status to show completion
|
|
6073
|
+
this.renderer?.setActivity('✅ Complete');
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
// AlphaZero: Check for failure in response
|
|
6077
|
+
const failure = detectFailure(responseText, {
|
|
6078
|
+
toolCalls: this.currentToolCalls,
|
|
6079
|
+
userMessage: request,
|
|
6080
|
+
});
|
|
6081
|
+
if (failure) {
|
|
6082
|
+
this.lastFailure = failure;
|
|
6083
|
+
// Check if we have a recovery strategy
|
|
6084
|
+
const strategy = findRecoveryStrategy(failure);
|
|
6085
|
+
if (strategy) {
|
|
6086
|
+
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
else {
|
|
6090
|
+
// Success - record the tool pattern for this task type
|
|
6091
|
+
if (this.currentToolCalls.length > 0) {
|
|
6092
|
+
const toolPattern = {
|
|
6093
|
+
taskType: this.currentTaskType,
|
|
6094
|
+
toolSequence: this.currentToolCalls.map(t => t.name),
|
|
6095
|
+
successRate: 1.0,
|
|
6096
|
+
avgDuration: elapsedMs,
|
|
6097
|
+
occurrences: 1,
|
|
6098
|
+
};
|
|
6099
|
+
addToolPattern(this.currentTaskType, toolPattern);
|
|
6100
|
+
}
|
|
6101
|
+
// Clear action history on success
|
|
6102
|
+
clearActionHistory();
|
|
6103
|
+
this.lastFailure = null;
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
catch (error) {
|
|
6107
|
+
const handled = this.handleProviderError(error, () => this.processRequest(request, options));
|
|
6108
|
+
if (!handled) {
|
|
6109
|
+
// Pass full error object for enhanced formatting with stack trace
|
|
6110
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
6111
|
+
}
|
|
6112
|
+
}
|
|
6113
|
+
finally {
|
|
6114
|
+
// Fallback: if no assistant message was rendered (e.g., streaming hiccup), show the full response
|
|
6115
|
+
if (!this.responseRendered && responseText.trim()) {
|
|
6116
|
+
const finalText = responseText.trim();
|
|
5919
6117
|
display.showAssistantMessage(finalText, { isFinal: true });
|
|
5920
6118
|
this.ui.controller.recordAssistantResponse(finalText, {
|
|
5921
6119
|
source: 'final',
|
|
5922
6120
|
});
|
|
5923
6121
|
this.responseRendered = true;
|
|
5924
6122
|
}
|
|
5925
|
-
result = {
|
|
5926
|
-
finalResponse: lastResponseText,
|
|
5927
|
-
toolsUsed: lastToolsUsed,
|
|
5928
|
-
elapsedMs: Date.now() - overallStartTime,
|
|
5929
|
-
};
|
|
5930
|
-
}
|
|
5931
|
-
finally {
|
|
5932
6123
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5933
|
-
const totalElapsed = Date.now() - overallStartTime;
|
|
5934
|
-
const minutes = Math.floor(totalElapsed / 60000);
|
|
5935
|
-
const seconds = Math.floor((totalElapsed % 60000) / 1000);
|
|
5936
|
-
const completionLabel = mode === 'continuous'
|
|
5937
|
-
? `\n🏁 Continuous execution completed in ${minutes}m ${seconds}s total`
|
|
5938
|
-
: `\n🏁 Flow orchestration completed in ${minutes}m ${seconds}s total`;
|
|
5939
|
-
display.showSystemMessage(completionLabel);
|
|
5940
|
-
resetTaskCompletionDetector();
|
|
5941
6124
|
display.stopThinking(false);
|
|
5942
6125
|
this.uiUpdates.setMode('processing');
|
|
5943
6126
|
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
@@ -5950,60 +6133,20 @@ The previous reply was empty. Resume the task now.`;
|
|
|
5950
6133
|
this.updateStatusMessage(null);
|
|
5951
6134
|
this.toolsUsedThisRun = [];
|
|
5952
6135
|
queueMicrotask(() => this.uiUpdates.setMode('idle'));
|
|
6136
|
+
// CRITICAL: Ensure readline prompt is active for user input
|
|
6137
|
+
// Erosolar-CLI style: New prompt naturally appears at bottom
|
|
5953
6138
|
this.ensureReadlineReady();
|
|
5954
6139
|
this.scheduleQueueProcessing();
|
|
5955
6140
|
this.maybeProcessPromptInbox();
|
|
5956
6141
|
this.refreshQueueIndicators();
|
|
5957
6142
|
}
|
|
5958
|
-
return result;
|
|
5959
|
-
}
|
|
5960
|
-
handleFlowRunOutcome(request, result) {
|
|
5961
|
-
this.currentToolCalls = result.toolsUsed.map((name) => ({
|
|
5962
|
-
name,
|
|
5963
|
-
arguments: {},
|
|
5964
|
-
success: true,
|
|
5965
|
-
duration: 0,
|
|
5966
|
-
}));
|
|
5967
|
-
const failure = detectFailure(result.finalResponse, {
|
|
5968
|
-
toolCalls: this.currentToolCalls,
|
|
5969
|
-
userMessage: request,
|
|
5970
|
-
});
|
|
5971
|
-
if (failure) {
|
|
5972
|
-
this.lastFailure = failure;
|
|
5973
|
-
const strategy = findRecoveryStrategy(failure);
|
|
5974
|
-
if (strategy) {
|
|
5975
|
-
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
5976
|
-
}
|
|
5977
|
-
return;
|
|
5978
|
-
}
|
|
5979
|
-
if (this.currentToolCalls.length > 0) {
|
|
5980
|
-
const toolPattern = {
|
|
5981
|
-
taskType: this.currentTaskType,
|
|
5982
|
-
toolSequence: this.currentToolCalls.map((t) => t.name),
|
|
5983
|
-
successRate: 1.0,
|
|
5984
|
-
avgDuration: result.elapsedMs,
|
|
5985
|
-
occurrences: 1,
|
|
5986
|
-
};
|
|
5987
|
-
addToolPattern(this.currentTaskType, toolPattern);
|
|
5988
|
-
}
|
|
5989
|
-
clearActionHistory();
|
|
5990
|
-
this.lastFailure = null;
|
|
5991
|
-
}
|
|
5992
|
-
async processRequest(request) {
|
|
5993
|
-
const result = await this.runFlowControlledTask(request, {
|
|
5994
|
-
mode: 'standard',
|
|
5995
|
-
});
|
|
5996
|
-
if (!result) {
|
|
5997
|
-
return;
|
|
5998
|
-
}
|
|
5999
|
-
this.handleFlowRunOutcome(request, result);
|
|
6000
6143
|
}
|
|
6001
6144
|
/**
|
|
6002
6145
|
* Process a continuous/infinite loop request.
|
|
6003
6146
|
* Runs the agent in a loop until:
|
|
6004
6147
|
* 1. The agent indicates completion (verified by AI confirmation)
|
|
6005
6148
|
* 2. User interrupts (Ctrl+C)
|
|
6006
|
-
* 3.
|
|
6149
|
+
* 3. Maximum iterations reached (safety limit)
|
|
6007
6150
|
*
|
|
6008
6151
|
* Uses intelligent task completion detection with AI verification
|
|
6009
6152
|
* to ensure tasks are truly complete before stopping.
|
|
@@ -6011,13 +6154,29 @@ The previous reply was empty. Resume the task now.`;
|
|
|
6011
6154
|
* Context is automatically managed - overflow errors trigger auto-recovery.
|
|
6012
6155
|
*/
|
|
6013
6156
|
async processContinuousRequest(initialRequest) {
|
|
6014
|
-
const
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6157
|
+
const MAX_PASSES = 100; // Safety limit to prevent truly infinite loops
|
|
6158
|
+
if (this.isProcessing) {
|
|
6159
|
+
this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
6162
|
+
if (!this.agent && !this.rebuildAgent()) {
|
|
6163
|
+
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
6018
6164
|
return;
|
|
6019
6165
|
}
|
|
6020
|
-
|
|
6166
|
+
display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
|
|
6167
|
+
const preparedRequest = this.isSelfImprovementRequest(initialRequest)
|
|
6168
|
+
? `${initialRequest}
|
|
6169
|
+
|
|
6170
|
+
IMPORTANT: You have full git access. After making improvements:
|
|
6171
|
+
1. Use bash to run: git status (see changes)
|
|
6172
|
+
2. Use bash to run: git add -A (stage changes)
|
|
6173
|
+
3. Use bash to run: git commit -m "descriptive message" (commit)
|
|
6174
|
+
4. Use bash to run: git push (when milestone reached)
|
|
6175
|
+
|
|
6176
|
+
Commit frequently with descriptive messages. Push when ready.
|
|
6177
|
+
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
6178
|
+
: initialRequest;
|
|
6179
|
+
await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
|
|
6021
6180
|
}
|
|
6022
6181
|
/**
|
|
6023
6182
|
* Resolve executed tools for the current turn. Prefer the actual tool
|
|
@@ -6039,6 +6198,129 @@ The previous reply was empty. Resume the task now.`;
|
|
|
6039
6198
|
}
|
|
6040
6199
|
return this.extractToolsFromResponse(responseText);
|
|
6041
6200
|
}
|
|
6201
|
+
/**
|
|
6202
|
+
* Detect plan-only responses that narrate intent without executing actions.
|
|
6203
|
+
*/
|
|
6204
|
+
isPlanOnlyResponse(response) {
|
|
6205
|
+
const normalized = response.trim().toLowerCase();
|
|
6206
|
+
if (!normalized) {
|
|
6207
|
+
return false;
|
|
6208
|
+
}
|
|
6209
|
+
// If the assistant is clearly declaring completion, don't treat it as plan-only
|
|
6210
|
+
const completionGuards = [
|
|
6211
|
+
/\bnothing\s+(left|else)\s+(to\s+do|pending)\b/i,
|
|
6212
|
+
/\b(already|now)\s+(clean|complete|done)\b/i,
|
|
6213
|
+
/\b(no\s+(junk|issues?|changes?)\s+found)\b/i,
|
|
6214
|
+
];
|
|
6215
|
+
if (completionGuards.some((pattern) => pattern.test(response))) {
|
|
6216
|
+
return false;
|
|
6217
|
+
}
|
|
6218
|
+
const planIndicators = [
|
|
6219
|
+
/\bplan\b/i,
|
|
6220
|
+
/\bapproach\b/i,
|
|
6221
|
+
/\bsteps?:\b/i,
|
|
6222
|
+
/\bstep\s+1\b/i,
|
|
6223
|
+
/\bstart by\b/i,
|
|
6224
|
+
/\bfirst[, ]/i,
|
|
6225
|
+
/\bthen\b/i,
|
|
6226
|
+
/\bnext\b/i,
|
|
6227
|
+
/\bi['’]?\s*will\b/i,
|
|
6228
|
+
/\bi['’]?\s*ll\b/i,
|
|
6229
|
+
/\bi['’]?\s*can\b.{0,40}\bthen\b/i,
|
|
6230
|
+
/\bi['’]?\s*(?:will|ll)\s+begin\b/i,
|
|
6231
|
+
];
|
|
6232
|
+
return planIndicators.some((pattern) => pattern.test(response));
|
|
6233
|
+
}
|
|
6234
|
+
/**
|
|
6235
|
+
* Check if a response contains indicators that work is actually incomplete,
|
|
6236
|
+
* even if it also contains TASK_FULLY_COMPLETE marker.
|
|
6237
|
+
* This catches contradictory responses where the AI says "done" but also "not integrated yet".
|
|
6238
|
+
*/
|
|
6239
|
+
responseIndicatesIncompleteWork(response) {
|
|
6240
|
+
// Patterns that indicate work isn't actually complete
|
|
6241
|
+
// Organized by category for maintainability
|
|
6242
|
+
const incompletePatterns = [
|
|
6243
|
+
// === INTEGRATION/DEPLOYMENT STATE ===
|
|
6244
|
+
// "hasn't been integrated/implemented/connected yet"
|
|
6245
|
+
/hasn'?t\s+been\s+(integrated|implemented|connected|deployed|added|completed|tested|verified)\s*(yet|still)?/i,
|
|
6246
|
+
// "not yet integrated/implemented" or "not integrated"
|
|
6247
|
+
/not\s+(yet\s+)?(integrated|implemented|connected|deployed|functional|working|complete|tested|verified)/i,
|
|
6248
|
+
// "ready for integration" = NOT integrated
|
|
6249
|
+
/ready\s+(for|to\s+be)\s+(integration|integrated|connected|deployed|testing|review)/i,
|
|
6250
|
+
// "needs to be integrated"
|
|
6251
|
+
/needs?\s+to\s+be\s+(integrated|connected|deployed|added|hooked|wired|tested|reviewed|merged)/i,
|
|
6252
|
+
// Passive voice: "was not performed/completed"
|
|
6253
|
+
/was\s+not\s+(performed|completed|implemented|deployed|integrated|tested)/i,
|
|
6254
|
+
// "the [X] service hasn't been"
|
|
6255
|
+
/the\s+\w+\s+(service|module|component|feature)\s+hasn'?t\s+been/i,
|
|
6256
|
+
// === PARTIAL/INCOMPLETE STATE ===
|
|
6257
|
+
// "still stores/uses/has" (current bad state persists)
|
|
6258
|
+
/still\s+(stores?|uses?|has|contains?|needs?|requires?|missing|lacks?|broken)/i,
|
|
6259
|
+
// Partial completion: "partially", "mostly", "almost"
|
|
6260
|
+
/\b(partially|mostly|almost|nearly|not\s+fully)\s+(complete|done|finished|implemented|working)/i,
|
|
6261
|
+
// Explicit partial: "part of", "some of", "half of"
|
|
6262
|
+
/\b(only\s+)?(part|some|half|portion)\s+of\s+(the\s+)?(task|work|feature|implementation)/i,
|
|
6263
|
+
// === QUALIFIER WORDS (uncertain completion) ===
|
|
6264
|
+
// "should be complete", "appears complete", "theoretically"
|
|
6265
|
+
/\b(should|might|may|could|appears?\s+to)\s+be\s+(complete|done|working|functional)/i,
|
|
6266
|
+
/\btheoretically\s+(complete|done|working|functional)/i,
|
|
6267
|
+
// "assuming", "if everything works"
|
|
6268
|
+
/\b(assuming|provided|if)\s+(everything|it|this|that)\s+(works?|is\s+correct)/i,
|
|
6269
|
+
// === SELF-CONTRADICTION PHRASES ===
|
|
6270
|
+
// "done but...", "complete except...", "finished however..."
|
|
6271
|
+
/\b(done|complete|finished)\s+(but|except|however|although|though)/i,
|
|
6272
|
+
// "however" followed by incomplete indicator
|
|
6273
|
+
/however[,\s].{0,50}?(hasn'?t|not\s+yet|still\s+needs?|pending|remains?|missing|broken|failing)/i,
|
|
6274
|
+
// "but" followed by negative state
|
|
6275
|
+
/\bbut\s+.{0,30}?(not|hasn'?t|won'?t|can'?t|doesn'?t|isn'?t|wasn'?t)/i,
|
|
6276
|
+
// === FUTURE TENSE / DEFERRED WORK ===
|
|
6277
|
+
// "will need to", "will require"
|
|
6278
|
+
/will\s+(need\s+to|require|have\s+to)\s+(integrate|connect|deploy|complete|implement|test|fix)/i,
|
|
6279
|
+
// Deferred: "left as", "deferred", "postponed", "out of scope"
|
|
6280
|
+
/\b(left\s+as|deferred|postponed|out\s+of\s+scope|for\s+later|in\s+a\s+future)/i,
|
|
6281
|
+
// Time-dependent: "after restart", "takes effect after", "once you"
|
|
6282
|
+
/\b(after\s+(restart|reboot|redeploy)|takes?\s+effect\s+after|once\s+you)/i,
|
|
6283
|
+
// === REMAINING WORK INDICATORS ===
|
|
6284
|
+
// "remaining tasks", "outstanding items"
|
|
6285
|
+
/\b(remaining|outstanding|pending|leftover)\s+(tasks?|items?|work|issues?|steps?)/i,
|
|
6286
|
+
// "X more to do", "still have to"
|
|
6287
|
+
/\b(more\s+to\s+do|still\s+have\s+to|yet\s+to\s+be\s+done)/i,
|
|
6288
|
+
// Explicit blockers
|
|
6289
|
+
/\b(blocker|blocked\s+by|waiting\s+(for|on)|depends?\s+on)/i,
|
|
6290
|
+
// === ERROR/FAILURE STATE ===
|
|
6291
|
+
// "failing tests", "build errors"
|
|
6292
|
+
/\b(failing|broken|erroring)\s+(tests?|builds?|checks?|validations?)/i,
|
|
6293
|
+
// "tests? (are )?(still )?failing"
|
|
6294
|
+
/\btests?\s+(are\s+)?(still\s+)?failing/i,
|
|
6295
|
+
// "errors? to (address|fix)"
|
|
6296
|
+
/\b(errors?|warnings?|issues?)\s+to\s+(address|fix|resolve)/i,
|
|
6297
|
+
// "doesn't work", "isn't working", "not working"
|
|
6298
|
+
/\b(doesn'?t|isn'?t|not)\s+(work|working|functional|functioning)/i,
|
|
6299
|
+
// === MANUAL STEPS REQUIRED ===
|
|
6300
|
+
// "you'll need to", "manually run", "requires user"
|
|
6301
|
+
/\b(you('ll|\s+will)\s+need\s+to|manually\s+(run|configure|set|update)|requires?\s+user)/i,
|
|
6302
|
+
// "run this command", "execute the following"
|
|
6303
|
+
/\b(run\s+this|execute\s+the\s+following|apply\s+the\s+migration)/i,
|
|
6304
|
+
// === TODO/FIXME IN PROSE ===
|
|
6305
|
+
// TODO or FIXME mentioned as remaining work (not in code blocks)
|
|
6306
|
+
/\b(todo|fixme|hack|xxx):\s/i,
|
|
6307
|
+
// "need to add", "should implement"
|
|
6308
|
+
/\b(need\s+to|should|must)\s+(add|implement|create|write|build|fix)\b/i,
|
|
6309
|
+
// === SCOPE LIMITATIONS ===
|
|
6310
|
+
// "didn't have time", "ran out of time"
|
|
6311
|
+
/\b(didn'?t|did\s+not)\s+have\s+(time|chance|opportunity)/i,
|
|
6312
|
+
// "beyond scope", "outside scope"
|
|
6313
|
+
/\b(beyond|outside)\s+(the\s+)?scope/i,
|
|
6314
|
+
// "for now" (temporary state)
|
|
6315
|
+
/\b(for\s+now|at\s+this\s+point|currently)\s*.{0,20}?(not|without|lacks?|missing)/i,
|
|
6316
|
+
];
|
|
6317
|
+
for (const pattern of incompletePatterns) {
|
|
6318
|
+
if (pattern.test(response)) {
|
|
6319
|
+
return true;
|
|
6320
|
+
}
|
|
6321
|
+
}
|
|
6322
|
+
return false;
|
|
6323
|
+
}
|
|
6042
6324
|
/**
|
|
6043
6325
|
* Extract tool names from a response by looking for tool call patterns
|
|
6044
6326
|
*/
|
|
@@ -6531,12 +6813,11 @@ Return ONLY JSON array:
|
|
|
6531
6813
|
const activity = normalized ? `Working: ${normalized}` : 'Working';
|
|
6532
6814
|
this.renderer?.setActivity(activity);
|
|
6533
6815
|
},
|
|
6534
|
-
onBeforeFirstToolCall: (toolNames
|
|
6816
|
+
onBeforeFirstToolCall: (toolNames) => {
|
|
6535
6817
|
const primaryTool = toolNames[0];
|
|
6536
6818
|
if (primaryTool) {
|
|
6537
6819
|
this.renderer?.setActivity(`Running ${primaryTool}`);
|
|
6538
6820
|
}
|
|
6539
|
-
// Don't inject synthetic thinking blocks - let the model respond naturally
|
|
6540
6821
|
return undefined;
|
|
6541
6822
|
},
|
|
6542
6823
|
onStreamChunk: (chunk, type) => {
|
|
@@ -6685,14 +6966,10 @@ Return ONLY JSON array:
|
|
|
6685
6966
|
activity = `Reading ${path}`;
|
|
6686
6967
|
}
|
|
6687
6968
|
this.renderer?.setActivity(activity);
|
|
6688
|
-
//
|
|
6689
|
-
this.streamingTokenCount += 50;
|
|
6690
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6969
|
+
// Token count updated from real provider usage - do not estimate
|
|
6691
6970
|
}
|
|
6692
6971
|
else {
|
|
6693
|
-
// Tool finished -
|
|
6694
|
-
this.streamingTokenCount += 100;
|
|
6695
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6972
|
+
// Tool finished - token count updated from real provider usage
|
|
6696
6973
|
// Reset to thinking state while model generates next response
|
|
6697
6974
|
this.renderer?.setActivity('Thinking');
|
|
6698
6975
|
}
|
|
@@ -6701,6 +6978,14 @@ Return ONLY JSON array:
|
|
|
6701
6978
|
this.lastAssistantResponse = response;
|
|
6702
6979
|
void this.runAutoQualityChecks('verification', response, context);
|
|
6703
6980
|
},
|
|
6981
|
+
// Real token usage from provider during streaming
|
|
6982
|
+
onUsage: (usage) => {
|
|
6983
|
+
const totalTokens = this.totalTokens(usage);
|
|
6984
|
+
if (totalTokens !== null) {
|
|
6985
|
+
this.streamingTokenCount = totalTokens;
|
|
6986
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6987
|
+
}
|
|
6988
|
+
},
|
|
6704
6989
|
// Retry notification for transient errors
|
|
6705
6990
|
onRetrying: (attempt, maxAttempts, error) => {
|
|
6706
6991
|
const shortError = error.message.slice(0, 100);
|
|
@@ -6710,6 +6995,8 @@ Return ONLY JSON array:
|
|
|
6710
6995
|
});
|
|
6711
6996
|
// Register global AI enhancer for explore tool - uses active model by default
|
|
6712
6997
|
this.registerExploreAIEnhancer();
|
|
6998
|
+
// Fetch real context window from provider API (async, updates in background)
|
|
6999
|
+
void this.fetchAndUpdateContextWindow();
|
|
6713
7000
|
const allowHistoryRestore = this.sessionRestoreConfig.mode !== 'none';
|
|
6714
7001
|
const historyToLoad = allowHistoryRestore && this.pendingHistoryLoad && this.pendingHistoryLoad.length
|
|
6715
7002
|
? this.pendingHistoryLoad
|