erosolar-cli 2.1.192 → 2.1.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/agent.d.ts +11 -13
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +84 -33
- package/dist/core/agent.js.map +1 -1
- package/dist/core/schemaValidator.d.ts +0 -5
- package/dist/core/schemaValidator.d.ts.map +1 -1
- package/dist/core/schemaValidator.js +0 -65
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/toolRuntime.d.ts +0 -4
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +64 -40
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/runtime/flowOrchestrator.d.ts +46 -0
- package/dist/runtime/flowOrchestrator.d.ts.map +1 -0
- package/dist/runtime/flowOrchestrator.js +80 -0
- package/dist/runtime/flowOrchestrator.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +4 -19
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +189 -408
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.js +1 -8
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/taskCompletionDetector.d.ts +52 -0
- package/dist/shell/taskCompletionDetector.d.ts.map +1 -0
- package/dist/shell/taskCompletionDetector.js +131 -0
- package/dist/shell/taskCompletionDetector.js.map +1 -0
- package/dist/tools/bashTools.d.ts +1 -0
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +47 -3
- 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 +4 -6
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/repoChecksTools.d.ts.map +1 -1
- package/dist/tools/repoChecksTools.js +7 -5
- package/dist/tools/repoChecksTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +2 -3
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +7 -9
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/package.json +1 -1
- package/README.md +0 -54
- package/dist/contracts/models.schema.json +0 -9
- package/dist/core/agentOrchestrator.d.ts +0 -49
- package/dist/core/agentOrchestrator.d.ts.map +0 -1
- package/dist/core/agentOrchestrator.js +0 -313
- package/dist/core/agentOrchestrator.js.map +0 -1
- package/dist/core/alphaZeroOrchestrator.d.ts +0 -140
- package/dist/core/alphaZeroOrchestrator.d.ts.map +0 -1
- package/dist/core/alphaZeroOrchestrator.js +0 -418
- package/dist/core/alphaZeroOrchestrator.js.map +0 -1
- package/dist/core/taskCompletionDetector.d.ts +0 -106
- package/dist/core/taskCompletionDetector.d.ts.map +0 -1
- package/dist/core/taskCompletionDetector.js +0 -402
- package/dist/core/taskCompletionDetector.js.map +0 -1
|
@@ -4,7 +4,6 @@ import { exec } from 'node:child_process';
|
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
7
|
-
import { AgentOrchestrator } from '../core/agentOrchestrator.js';
|
|
8
7
|
import { display } from '../ui/display.js';
|
|
9
8
|
import { theme } from '../ui/theme.js';
|
|
10
9
|
import { getTerminalColumns } from '../ui/layout.js';
|
|
@@ -13,11 +12,13 @@ import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue
|
|
|
13
12
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
14
13
|
import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
|
|
15
14
|
import { buildEnabledToolSet, evaluateToolPermissions, getToolToggleOptions, } from '../capabilities/toolRegistry.js';
|
|
15
|
+
import { FlowOrchestrator } from '../runtime/flowOrchestrator.js';
|
|
16
16
|
import { detectApiKeyError } from '../core/errors/apiKeyErrors.js';
|
|
17
17
|
import { detectPromptBlockError, } from '../core/errors/promptBlockErrors.js';
|
|
18
18
|
import { detectNetworkError } from '../core/errors/networkErrors.js';
|
|
19
19
|
import { buildWorkspaceContext } from '../workspace.js';
|
|
20
20
|
import { buildInteractiveSystemPrompt } from './systemPrompt.js';
|
|
21
|
+
import { getTaskCompletionDetector, resetTaskCompletionDetector, WRITE_TOOLS, } from './taskCompletionDetector.js';
|
|
21
22
|
import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
|
|
22
23
|
import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
|
|
23
24
|
import { loadMcpServers } from '../mcp/config.js';
|
|
@@ -85,6 +86,10 @@ const CONTEXT_CLEANUP_SYSTEM_PROMPT = `Summarize earlier IDE collaboration so th
|
|
|
85
86
|
- Respond in plain Markdown only (no tool or shell calls).`;
|
|
86
87
|
const MAX_ATTACHMENT_BYTES = 200 * 1024; // 200KB per attachment
|
|
87
88
|
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')));
|
|
88
93
|
export class InteractiveShell {
|
|
89
94
|
agent = null;
|
|
90
95
|
profile;
|
|
@@ -119,6 +124,7 @@ export class InteractiveShell {
|
|
|
119
124
|
uiUpdates;
|
|
120
125
|
_fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
|
|
121
126
|
alphaZeroMetrics; // Alpha Zero 2 performance tracking
|
|
127
|
+
flowOrchestrator = new FlowOrchestrator();
|
|
122
128
|
maxNetworkRetries = 2;
|
|
123
129
|
statusSubscription = null;
|
|
124
130
|
followUpQueue = [];
|
|
@@ -134,7 +140,6 @@ export class InteractiveShell {
|
|
|
134
140
|
lastContextWarningLevel = null;
|
|
135
141
|
sessionPreferences;
|
|
136
142
|
autosaveEnabled;
|
|
137
|
-
orchestrationNoticeShown = false;
|
|
138
143
|
verificationEnabled = false;
|
|
139
144
|
criticalApprovalMode = 'auto';
|
|
140
145
|
editGuardMode = 'display-edits';
|
|
@@ -1992,8 +1997,9 @@ export class InteractiveShell {
|
|
|
1992
1997
|
return;
|
|
1993
1998
|
}
|
|
1994
1999
|
const isReasoning = type === 'reasoning';
|
|
1995
|
-
//
|
|
1996
|
-
|
|
2000
|
+
// Approximate token count (roughly 4 chars per token)
|
|
2001
|
+
this.streamingTokenCount += Math.ceil(chunk.length / 4);
|
|
2002
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
1997
2003
|
// Keep pinned status updated for all streaming chunks
|
|
1998
2004
|
this.updateStreamingStatusFromChunk(chunk);
|
|
1999
2005
|
// Handle <thinking> tags as separate events in the queue
|
|
@@ -2270,17 +2276,14 @@ export class InteractiveShell {
|
|
|
2270
2276
|
// Check for continuous/infinite loop commands or auto-escalation to completion mode
|
|
2271
2277
|
const explicitContinuous = this.isContinuousCommand(trimmed);
|
|
2272
2278
|
const autoContinuous = this.shouldAutoRunToCompletion(trimmed);
|
|
2273
|
-
if (explicitContinuous) {
|
|
2279
|
+
if (explicitContinuous || autoContinuous) {
|
|
2280
|
+
if (autoContinuous && !explicitContinuous) {
|
|
2281
|
+
display.showSystemMessage('⚡ Actionable request detected; running continuously until complete (Ctrl+C to stop).');
|
|
2282
|
+
}
|
|
2274
2283
|
await this.processContinuousRequest(trimmed);
|
|
2275
2284
|
this.syncRendererInput();
|
|
2276
2285
|
return;
|
|
2277
2286
|
}
|
|
2278
|
-
if (autoContinuous) {
|
|
2279
|
-
display.showSystemMessage('⚡ Actionable request detected; orchestrating until complete (Ctrl+C to stop).');
|
|
2280
|
-
await this.processRequest(trimmed, { orchestrate: true });
|
|
2281
|
-
this.syncRendererInput();
|
|
2282
|
-
return;
|
|
2283
|
-
}
|
|
2284
2287
|
// Direct execution for all inputs, including multi-line pastes
|
|
2285
2288
|
await this.processRequest(trimmed);
|
|
2286
2289
|
this.syncRendererInput();
|
|
@@ -2317,113 +2320,19 @@ export class InteractiveShell {
|
|
|
2317
2320
|
];
|
|
2318
2321
|
const strongMaintenanceIntent = maintenancePatterns.some((pattern) => pattern.test(normalized));
|
|
2319
2322
|
// General action intent in a code context
|
|
2320
|
-
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
|
|
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
2324
|
const codeContext = /\b(repo|codebase|project|app|service|package|module|component|workspace|cli|tests?|source|files?|artifacts?)\b/;
|
|
2322
|
-
const
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
// Score-based intent detection keeps questions like "what is this repo?" out
|
|
2331
|
-
let actionScore = 0;
|
|
2332
|
-
if (strongMaintenanceIntent)
|
|
2333
|
-
actionScore += 2;
|
|
2334
|
-
if (bugOrHealthIntent)
|
|
2335
|
-
actionScore += 2;
|
|
2336
|
-
if (hasActionVerb)
|
|
2337
|
-
actionScore += 1;
|
|
2338
|
-
if (hasCodeContext)
|
|
2339
|
-
actionScore += 1;
|
|
2340
|
-
const decisiveAction = actionScore >= 2 || (actionScore === 1 && !isQuestion && normalized.length > 10);
|
|
2341
|
-
if (!decisiveAction) {
|
|
2342
|
-
return false;
|
|
2343
|
-
}
|
|
2344
|
-
// Respect informational openers unless intent is clearly actionable
|
|
2345
|
-
if (startsWithInfoWord && actionScore < 3 && !bugOrHealthIntent && !strongMaintenanceIntent) {
|
|
2346
|
-
return false;
|
|
2347
|
-
}
|
|
2348
|
-
return true;
|
|
2349
|
-
}
|
|
2350
|
-
shouldContinueOrchestrating(originalRequest, result) {
|
|
2351
|
-
if (!result) {
|
|
2352
|
-
return { shouldContinue: false, reason: null };
|
|
2353
|
-
}
|
|
2354
|
-
const actionable = this.shouldAutoRunToCompletion(originalRequest);
|
|
2355
|
-
if (!actionable) {
|
|
2356
|
-
const incomplete = result.exitReason !== 'complete';
|
|
2357
|
-
return { shouldContinue: incomplete, reason: incomplete ? result.exitReason : null };
|
|
2358
|
-
}
|
|
2359
|
-
if (result.exitReason !== 'complete') {
|
|
2360
|
-
return { shouldContinue: true, reason: result.exitReason };
|
|
2361
|
-
}
|
|
2362
|
-
const executedTools = result.passes.some(pass => (pass.toolsUsed?.length ?? 0) > 0);
|
|
2363
|
-
const lastPass = result.passes[result.passes.length - 1];
|
|
2364
|
-
const lastPlanOnly = Boolean(lastPass?.planOnly) && !lastPass?.tookAction && (!lastPass?.toolsUsed?.length);
|
|
2365
|
-
const lastEmpty = !lastPass?.response?.trim();
|
|
2366
|
-
if (!executedTools) {
|
|
2367
|
-
return { shouldContinue: true, reason: 'no-action' };
|
|
2368
|
-
}
|
|
2369
|
-
if (lastPlanOnly) {
|
|
2370
|
-
return { shouldContinue: true, reason: 'plan-only' };
|
|
2371
|
-
}
|
|
2372
|
-
if (lastEmpty) {
|
|
2373
|
-
return { shouldContinue: true, reason: 'empty-response' };
|
|
2374
|
-
}
|
|
2375
|
-
return { shouldContinue: false, reason: null };
|
|
2376
|
-
}
|
|
2377
|
-
describeContinuationReason(reason) {
|
|
2378
|
-
switch (reason) {
|
|
2379
|
-
case 'no-action':
|
|
2380
|
-
return 'No tools or concrete actions were executed; continuing until real work is done.';
|
|
2381
|
-
case 'plan-only':
|
|
2382
|
-
return 'Last pass was just planning; executing the next concrete step now.';
|
|
2383
|
-
case 'empty-response':
|
|
2384
|
-
case 'empty':
|
|
2385
|
-
return 'Previous pass returned nothing; resuming with a concrete action.';
|
|
2386
|
-
case 'stalled':
|
|
2387
|
-
return 'Previous orchestration stalled; forcing continuation.';
|
|
2388
|
-
case 'max-passes':
|
|
2389
|
-
return 'Reached pass limit; extending the run to finish the task.';
|
|
2390
|
-
default:
|
|
2391
|
-
return 'Continuing orchestration until the task is actually finished.';
|
|
2325
|
+
const strongActionIntent = actionVerb.test(normalized) && codeContext.test(normalized) && normalized.length > 20;
|
|
2326
|
+
// Allow maintenance/refactor tasks to auto-run even if phrased as questions ("got any junk to remove?")
|
|
2327
|
+
if (strongMaintenanceIntent || strongActionIntent) {
|
|
2328
|
+
// But still avoid purely informational questions like "what is this repo?"
|
|
2329
|
+
if (startsWithInfoWord && !strongMaintenanceIntent) {
|
|
2330
|
+
return false;
|
|
2331
|
+
}
|
|
2332
|
+
return true;
|
|
2392
2333
|
}
|
|
2334
|
+
return false;
|
|
2393
2335
|
}
|
|
2394
|
-
buildForcedContinuationPrompt(originalRequest, lastResult, continuationReason) {
|
|
2395
|
-
const lastPass = lastResult.passes[lastResult.passes.length - 1];
|
|
2396
|
-
const rawResponse = lastPass?.response?.trim() ?? '';
|
|
2397
|
-
const truncatedResponse = rawResponse ? rawResponse.slice(0, 1200) : '';
|
|
2398
|
-
const responseNote = rawResponse && rawResponse.length > truncatedResponse.length
|
|
2399
|
-
? '\n\n[Last response truncated]'
|
|
2400
|
-
: '';
|
|
2401
|
-
const usedTools = lastResult.passes.flatMap(pass => pass.toolsUsed ?? []);
|
|
2402
|
-
const hasToolUsage = usedTools.length > 0;
|
|
2403
|
-
const toolsUsed = hasToolUsage
|
|
2404
|
-
? `Tools used so far: ${usedTools.join(', ')}.`
|
|
2405
|
-
: 'No tools have been used yet - start with a quick workspace scan (list_files + glob/grep/search) and then take concrete actions now.';
|
|
2406
|
-
const responseSection = truncatedResponse
|
|
2407
|
-
? `Last response snapshot:\n${truncatedResponse}${responseNote}\n`
|
|
2408
|
-
: '';
|
|
2409
|
-
const exitReason = continuationReason ?? lastResult.exitReason ?? 'incomplete';
|
|
2410
|
-
const reasonLine = this.describeContinuationReason(exitReason);
|
|
2411
|
-
const missingActionLine = hasToolUsage
|
|
2412
|
-
? ''
|
|
2413
|
-
: '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.';
|
|
2414
|
-
return `${originalRequest.trim()}
|
|
2415
|
-
|
|
2416
|
-
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.
|
|
2417
|
-
|
|
2418
|
-
${reasonLine}
|
|
2419
|
-
${toolsUsed}
|
|
2420
|
-
${responseSection}
|
|
2421
|
-
${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concrete action now.`;
|
|
2422
|
-
}
|
|
2423
|
-
/**
|
|
2424
|
-
* Decide whether to automatically continue execution when the model stops after a plan/summary
|
|
2425
|
-
* without taking actions. This keeps flows moving toward completion instead of stalling on planning.
|
|
2426
|
-
*/
|
|
2427
2336
|
isExitCommand(input) {
|
|
2428
2337
|
const lower = input.trim().toLowerCase();
|
|
2429
2338
|
return (lower === 'exit' ||
|
|
@@ -5871,20 +5780,22 @@ ${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concret
|
|
|
5871
5780
|
this.clearInlinePanel();
|
|
5872
5781
|
this.syncRendererInput();
|
|
5873
5782
|
}
|
|
5874
|
-
async
|
|
5783
|
+
async runFlowControlledTask(initialRequest, options) {
|
|
5784
|
+
const { mode } = options;
|
|
5785
|
+
const followUpType = mode === 'continuous' ? 'continuous' : 'request';
|
|
5875
5786
|
if (this.isProcessing) {
|
|
5876
|
-
this.enqueueFollowUpAction({ type:
|
|
5877
|
-
return;
|
|
5787
|
+
this.enqueueFollowUpAction({ type: followUpType, text: initialRequest });
|
|
5788
|
+
return null;
|
|
5878
5789
|
}
|
|
5879
5790
|
if (!this.agent && !this.rebuildAgent()) {
|
|
5880
5791
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
5881
|
-
return;
|
|
5792
|
+
return null;
|
|
5882
5793
|
}
|
|
5883
5794
|
this.inlinePanelScopeActive = false;
|
|
5884
5795
|
this.clearInlinePanel();
|
|
5885
5796
|
const agent = this.agent;
|
|
5886
5797
|
if (!agent) {
|
|
5887
|
-
return;
|
|
5798
|
+
return null;
|
|
5888
5799
|
}
|
|
5889
5800
|
this.toolsUsedThisRun = [];
|
|
5890
5801
|
this.currentToolCalls = [];
|
|
@@ -5895,166 +5806,138 @@ ${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concret
|
|
|
5895
5806
|
else {
|
|
5896
5807
|
this.resetNetworkRetryState();
|
|
5897
5808
|
}
|
|
5898
|
-
// Reset per-request render tracking
|
|
5899
5809
|
this.responseRendered = false;
|
|
5900
|
-
if (this.shouldLogPrompt(
|
|
5901
|
-
this.logUserPrompt(
|
|
5810
|
+
if (this.shouldLogPrompt(initialRequest)) {
|
|
5811
|
+
this.logUserPrompt(initialRequest);
|
|
5902
5812
|
}
|
|
5903
5813
|
this.isProcessing = true;
|
|
5904
5814
|
this.uiUpdates.setMode('processing');
|
|
5905
|
-
this.streamingTokenCount = 0;
|
|
5815
|
+
this.streamingTokenCount = 0;
|
|
5906
5816
|
this.terminalInput.setStreaming(true);
|
|
5907
|
-
// Keep the persistent input/control bar active as we transition into streaming.
|
|
5908
5817
|
this.syncRendererInput();
|
|
5909
5818
|
this.renderer?.render();
|
|
5910
|
-
const
|
|
5911
|
-
// Clear previous parallel agents and start fresh for new request
|
|
5819
|
+
const overallStartTime = Date.now();
|
|
5912
5820
|
const parallelManager = getParallelAgentManager();
|
|
5913
5821
|
parallelManager.clear();
|
|
5914
5822
|
parallelManager.startBatch();
|
|
5915
|
-
|
|
5916
|
-
this.
|
|
5917
|
-
this.currentTaskType = classifyTaskType(request);
|
|
5823
|
+
this.lastUserQuery = initialRequest;
|
|
5824
|
+
this.currentTaskType = classifyTaskType(initialRequest);
|
|
5918
5825
|
this.currentToolCalls = [];
|
|
5919
5826
|
this.clearToolUsageMeta();
|
|
5920
5827
|
this.renderer?.setActivity('Starting...');
|
|
5921
|
-
this.uiAdapter.startProcessing('Working on your request');
|
|
5828
|
+
this.uiAdapter.startProcessing(mode === 'continuous' ? 'Continuous execution mode' : 'Working on your request');
|
|
5922
5829
|
this.setProcessingStatus();
|
|
5923
5830
|
this.beginAiRuntime();
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5831
|
+
this.startStreamingHeartbeat(mode === 'continuous' ? 'Streaming' : 'Streaming response');
|
|
5832
|
+
const completionDetector = getTaskCompletionDetector();
|
|
5833
|
+
completionDetector.reset();
|
|
5834
|
+
this.flowOrchestrator.start(initialRequest);
|
|
5835
|
+
this.updateStatusMessage('Orchestrating request...');
|
|
5836
|
+
if (mode === 'continuous') {
|
|
5837
|
+
display.showSystemMessage('Continuous mode active. Ctrl+C to stop.');
|
|
5931
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".`;
|
|
5847
|
+
}
|
|
5848
|
+
let iteration = 0;
|
|
5849
|
+
let lastResponseText = '';
|
|
5850
|
+
let lastToolsUsed = [];
|
|
5851
|
+
let result = null;
|
|
5932
5852
|
try {
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
if (!
|
|
5949
|
-
|
|
5853
|
+
while (true) {
|
|
5854
|
+
iteration++;
|
|
5855
|
+
this.toolsUsedThisRun = [];
|
|
5856
|
+
this.updateStatusMessage('Orchestrating request...');
|
|
5857
|
+
try {
|
|
5858
|
+
display.showThinking('Responding...');
|
|
5859
|
+
this.refreshStatusLine(true);
|
|
5860
|
+
const response = await agent.send(currentPrompt, true);
|
|
5861
|
+
lastResponseText = response ?? '';
|
|
5862
|
+
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5863
|
+
await this.awaitPendingCleanup();
|
|
5864
|
+
this.captureHistorySnapshot();
|
|
5865
|
+
this.autosaveIfEnabled();
|
|
5866
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
5867
|
+
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
5868
|
+
if (!response?.trim()) {
|
|
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;
|
|
5950
5874
|
}
|
|
5951
|
-
const
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
const
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
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,
|
|
5960
5887
|
});
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
exitReason: continuationResult.exitReason,
|
|
5965
|
-
};
|
|
5966
|
-
continuationRuns++;
|
|
5967
|
-
}
|
|
5968
|
-
responseText = orchestratorResult.finalResponse;
|
|
5969
|
-
}
|
|
5970
|
-
else {
|
|
5971
|
-
responseText = await agent.send(request, true);
|
|
5972
|
-
}
|
|
5973
|
-
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5974
|
-
await this.awaitPendingCleanup();
|
|
5975
|
-
this.captureHistorySnapshot();
|
|
5976
|
-
this.autosaveIfEnabled();
|
|
5977
|
-
// Track metrics with Alpha Zero 2
|
|
5978
|
-
const elapsedMs = Date.now() - requestStartTime;
|
|
5979
|
-
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
5980
|
-
if (!responseText?.trim()) {
|
|
5981
|
-
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
5982
|
-
}
|
|
5983
|
-
// AlphaZero: Extract and track tool calls from response
|
|
5984
|
-
const toolsUsed = orchestratorResult
|
|
5985
|
-
? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
|
|
5986
|
-
: this.getExecutedTools(responseText);
|
|
5987
|
-
this.currentToolCalls = toolsUsed.map(name => ({
|
|
5988
|
-
name,
|
|
5989
|
-
arguments: {},
|
|
5990
|
-
success: true, // Assume success if we got here
|
|
5991
|
-
duration: 0,
|
|
5992
|
-
}));
|
|
5993
|
-
if (orchestratorResult && orchestratorResult.exitReason !== 'complete') {
|
|
5994
|
-
const exitDetail = (() => {
|
|
5995
|
-
switch (orchestratorResult?.exitReason) {
|
|
5996
|
-
case 'max-passes':
|
|
5997
|
-
return 'Reached orchestrator pass limit; showing last response.';
|
|
5998
|
-
case 'empty-response':
|
|
5999
|
-
return 'Received empty replies while orchestrating; showing last response.';
|
|
6000
|
-
case 'stalled':
|
|
6001
|
-
return 'Orchestrator detected stagnation; showing last response.';
|
|
6002
|
-
default:
|
|
6003
|
-
return null;
|
|
5888
|
+
if (decision.type === 'stop') {
|
|
5889
|
+
display.showSystemMessage(decision.message);
|
|
5890
|
+
break;
|
|
6004
5891
|
}
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
this.lastFailure = failure;
|
|
6017
|
-
// Check if we have a recovery strategy
|
|
6018
|
-
const strategy = findRecoveryStrategy(failure);
|
|
6019
|
-
if (strategy) {
|
|
6020
|
-
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
5892
|
+
if (decision.type === 'stagnation-stop') {
|
|
5893
|
+
display.showWarning(decision.message);
|
|
5894
|
+
break;
|
|
5895
|
+
}
|
|
5896
|
+
if (decision.type === 'continue') {
|
|
5897
|
+
if (decision.message) {
|
|
5898
|
+
display.showSystemMessage(decision.message);
|
|
5899
|
+
}
|
|
5900
|
+
currentPrompt = decision.prompt;
|
|
5901
|
+
}
|
|
5902
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
6021
5903
|
}
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
}
|
|
6033
|
-
addToolPattern(this.currentTaskType, toolPattern);
|
|
5904
|
+
catch (error) {
|
|
5905
|
+
display.stopThinking(false);
|
|
5906
|
+
if (this.isContextOverflowError(error)) {
|
|
5907
|
+
display.showSystemMessage(`⚡ Context overflow handled. Continuing with reduced context...`);
|
|
5908
|
+
continue;
|
|
5909
|
+
}
|
|
5910
|
+
const handled = this.handleProviderError(error, () => this.runFlowControlledTask(initialRequest, options));
|
|
5911
|
+
if (!handled) {
|
|
5912
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
5913
|
+
break;
|
|
5914
|
+
}
|
|
6034
5915
|
}
|
|
6035
|
-
// Clear action history on success
|
|
6036
|
-
clearActionHistory();
|
|
6037
|
-
this.lastFailure = null;
|
|
6038
|
-
}
|
|
6039
|
-
}
|
|
6040
|
-
catch (error) {
|
|
6041
|
-
const handled = this.handleProviderError(error, () => this.processRequest(request, options));
|
|
6042
|
-
if (!handled) {
|
|
6043
|
-
// Pass full error object for enhanced formatting with stack trace
|
|
6044
|
-
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
6045
5916
|
}
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
// Fallback: if no assistant message was rendered (e.g., streaming hiccup), show the full response
|
|
6049
|
-
if (!this.responseRendered && responseText.trim()) {
|
|
6050
|
-
const finalText = responseText.trim();
|
|
5917
|
+
if (!this.responseRendered && lastResponseText.trim()) {
|
|
5918
|
+
const finalText = lastResponseText.trim();
|
|
6051
5919
|
display.showAssistantMessage(finalText, { isFinal: true });
|
|
6052
5920
|
this.ui.controller.recordAssistantResponse(finalText, {
|
|
6053
5921
|
source: 'final',
|
|
6054
5922
|
});
|
|
6055
5923
|
this.responseRendered = true;
|
|
6056
5924
|
}
|
|
5925
|
+
result = {
|
|
5926
|
+
finalResponse: lastResponseText,
|
|
5927
|
+
toolsUsed: lastToolsUsed,
|
|
5928
|
+
elapsedMs: Date.now() - overallStartTime,
|
|
5929
|
+
};
|
|
5930
|
+
}
|
|
5931
|
+
finally {
|
|
6057
5932
|
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();
|
|
6058
5941
|
display.stopThinking(false);
|
|
6059
5942
|
this.uiUpdates.setMode('processing');
|
|
6060
5943
|
this.stopStreamingHeartbeat('complete', { quiet: true });
|
|
@@ -6067,20 +5950,60 @@ ${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concret
|
|
|
6067
5950
|
this.updateStatusMessage(null);
|
|
6068
5951
|
this.toolsUsedThisRun = [];
|
|
6069
5952
|
queueMicrotask(() => this.uiUpdates.setMode('idle'));
|
|
6070
|
-
// CRITICAL: Ensure readline prompt is active for user input
|
|
6071
|
-
// Erosolar-CLI style: New prompt naturally appears at bottom
|
|
6072
5953
|
this.ensureReadlineReady();
|
|
6073
5954
|
this.scheduleQueueProcessing();
|
|
6074
5955
|
this.maybeProcessPromptInbox();
|
|
6075
5956
|
this.refreshQueueIndicators();
|
|
6076
5957
|
}
|
|
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);
|
|
6077
6000
|
}
|
|
6078
6001
|
/**
|
|
6079
6002
|
* Process a continuous/infinite loop request.
|
|
6080
6003
|
* Runs the agent in a loop until:
|
|
6081
6004
|
* 1. The agent indicates completion (verified by AI confirmation)
|
|
6082
6005
|
* 2. User interrupts (Ctrl+C)
|
|
6083
|
-
* 3.
|
|
6006
|
+
* 3. The orchestrator halts due to stagnation/verification exhaustion
|
|
6084
6007
|
*
|
|
6085
6008
|
* Uses intelligent task completion detection with AI verification
|
|
6086
6009
|
* to ensure tasks are truly complete before stopping.
|
|
@@ -6088,29 +6011,13 @@ ${missingActionLine ? `${missingActionLine}\n` : ''}Resume with the next concret
|
|
|
6088
6011
|
* Context is automatically managed - overflow errors trigger auto-recovery.
|
|
6089
6012
|
*/
|
|
6090
6013
|
async processContinuousRequest(initialRequest) {
|
|
6091
|
-
const
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
}
|
|
6096
|
-
if (!this.agent && !this.rebuildAgent()) {
|
|
6097
|
-
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
6014
|
+
const result = await this.runFlowControlledTask(initialRequest, {
|
|
6015
|
+
mode: 'continuous',
|
|
6016
|
+
});
|
|
6017
|
+
if (!result) {
|
|
6098
6018
|
return;
|
|
6099
6019
|
}
|
|
6100
|
-
|
|
6101
|
-
const preparedRequest = this.isSelfImprovementRequest(initialRequest)
|
|
6102
|
-
? `${initialRequest}
|
|
6103
|
-
|
|
6104
|
-
IMPORTANT: You have full git access. After making improvements:
|
|
6105
|
-
1. Use bash to run: git status (see changes)
|
|
6106
|
-
2. Use bash to run: git add -A (stage changes)
|
|
6107
|
-
3. Use bash to run: git commit -m "descriptive message" (commit)
|
|
6108
|
-
4. Use bash to run: git push (when milestone reached)
|
|
6109
|
-
|
|
6110
|
-
Commit frequently with descriptive messages. Push when ready.
|
|
6111
|
-
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
6112
|
-
: initialRequest;
|
|
6113
|
-
await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
|
|
6020
|
+
this.handleFlowRunOutcome(initialRequest, result);
|
|
6114
6021
|
}
|
|
6115
6022
|
/**
|
|
6116
6023
|
* Resolve executed tools for the current turn. Prefer the actual tool
|
|
@@ -6132,129 +6039,6 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6132
6039
|
}
|
|
6133
6040
|
return this.extractToolsFromResponse(responseText);
|
|
6134
6041
|
}
|
|
6135
|
-
/**
|
|
6136
|
-
* Detect plan-only responses that narrate intent without executing actions.
|
|
6137
|
-
*/
|
|
6138
|
-
isPlanOnlyResponse(response) {
|
|
6139
|
-
const normalized = response.trim().toLowerCase();
|
|
6140
|
-
if (!normalized) {
|
|
6141
|
-
return false;
|
|
6142
|
-
}
|
|
6143
|
-
// If the assistant is clearly declaring completion, don't treat it as plan-only
|
|
6144
|
-
const completionGuards = [
|
|
6145
|
-
/\bnothing\s+(left|else)\s+(to\s+do|pending)\b/i,
|
|
6146
|
-
/\b(already|now)\s+(clean|complete|done)\b/i,
|
|
6147
|
-
/\b(no\s+(junk|issues?|changes?)\s+found)\b/i,
|
|
6148
|
-
];
|
|
6149
|
-
if (completionGuards.some((pattern) => pattern.test(response))) {
|
|
6150
|
-
return false;
|
|
6151
|
-
}
|
|
6152
|
-
const planIndicators = [
|
|
6153
|
-
/\bplan\b/i,
|
|
6154
|
-
/\bapproach\b/i,
|
|
6155
|
-
/\bsteps?:\b/i,
|
|
6156
|
-
/\bstep\s+1\b/i,
|
|
6157
|
-
/\bstart by\b/i,
|
|
6158
|
-
/\bfirst[, ]/i,
|
|
6159
|
-
/\bthen\b/i,
|
|
6160
|
-
/\bnext\b/i,
|
|
6161
|
-
/\bi['’]?\s*will\b/i,
|
|
6162
|
-
/\bi['’]?\s*ll\b/i,
|
|
6163
|
-
/\bi['’]?\s*can\b.{0,40}\bthen\b/i,
|
|
6164
|
-
/\bi['’]?\s*(?:will|ll)\s+begin\b/i,
|
|
6165
|
-
];
|
|
6166
|
-
return planIndicators.some((pattern) => pattern.test(response));
|
|
6167
|
-
}
|
|
6168
|
-
/**
|
|
6169
|
-
* Check if a response contains indicators that work is actually incomplete,
|
|
6170
|
-
* even if it also contains TASK_FULLY_COMPLETE marker.
|
|
6171
|
-
* This catches contradictory responses where the AI says "done" but also "not integrated yet".
|
|
6172
|
-
*/
|
|
6173
|
-
responseIndicatesIncompleteWork(response) {
|
|
6174
|
-
// Patterns that indicate work isn't actually complete
|
|
6175
|
-
// Organized by category for maintainability
|
|
6176
|
-
const incompletePatterns = [
|
|
6177
|
-
// === INTEGRATION/DEPLOYMENT STATE ===
|
|
6178
|
-
// "hasn't been integrated/implemented/connected yet"
|
|
6179
|
-
/hasn'?t\s+been\s+(integrated|implemented|connected|deployed|added|completed|tested|verified)\s*(yet|still)?/i,
|
|
6180
|
-
// "not yet integrated/implemented" or "not integrated"
|
|
6181
|
-
/not\s+(yet\s+)?(integrated|implemented|connected|deployed|functional|working|complete|tested|verified)/i,
|
|
6182
|
-
// "ready for integration" = NOT integrated
|
|
6183
|
-
/ready\s+(for|to\s+be)\s+(integration|integrated|connected|deployed|testing|review)/i,
|
|
6184
|
-
// "needs to be integrated"
|
|
6185
|
-
/needs?\s+to\s+be\s+(integrated|connected|deployed|added|hooked|wired|tested|reviewed|merged)/i,
|
|
6186
|
-
// Passive voice: "was not performed/completed"
|
|
6187
|
-
/was\s+not\s+(performed|completed|implemented|deployed|integrated|tested)/i,
|
|
6188
|
-
// "the [X] service hasn't been"
|
|
6189
|
-
/the\s+\w+\s+(service|module|component|feature)\s+hasn'?t\s+been/i,
|
|
6190
|
-
// === PARTIAL/INCOMPLETE STATE ===
|
|
6191
|
-
// "still stores/uses/has" (current bad state persists)
|
|
6192
|
-
/still\s+(stores?|uses?|has|contains?|needs?|requires?|missing|lacks?|broken)/i,
|
|
6193
|
-
// Partial completion: "partially", "mostly", "almost"
|
|
6194
|
-
/\b(partially|mostly|almost|nearly|not\s+fully)\s+(complete|done|finished|implemented|working)/i,
|
|
6195
|
-
// Explicit partial: "part of", "some of", "half of"
|
|
6196
|
-
/\b(only\s+)?(part|some|half|portion)\s+of\s+(the\s+)?(task|work|feature|implementation)/i,
|
|
6197
|
-
// === QUALIFIER WORDS (uncertain completion) ===
|
|
6198
|
-
// "should be complete", "appears complete", "theoretically"
|
|
6199
|
-
/\b(should|might|may|could|appears?\s+to)\s+be\s+(complete|done|working|functional)/i,
|
|
6200
|
-
/\btheoretically\s+(complete|done|working|functional)/i,
|
|
6201
|
-
// "assuming", "if everything works"
|
|
6202
|
-
/\b(assuming|provided|if)\s+(everything|it|this|that)\s+(works?|is\s+correct)/i,
|
|
6203
|
-
// === SELF-CONTRADICTION PHRASES ===
|
|
6204
|
-
// "done but...", "complete except...", "finished however..."
|
|
6205
|
-
/\b(done|complete|finished)\s+(but|except|however|although|though)/i,
|
|
6206
|
-
// "however" followed by incomplete indicator
|
|
6207
|
-
/however[,\s].{0,50}?(hasn'?t|not\s+yet|still\s+needs?|pending|remains?|missing|broken|failing)/i,
|
|
6208
|
-
// "but" followed by negative state
|
|
6209
|
-
/\bbut\s+.{0,30}?(not|hasn'?t|won'?t|can'?t|doesn'?t|isn'?t|wasn'?t)/i,
|
|
6210
|
-
// === FUTURE TENSE / DEFERRED WORK ===
|
|
6211
|
-
// "will need to", "will require"
|
|
6212
|
-
/will\s+(need\s+to|require|have\s+to)\s+(integrate|connect|deploy|complete|implement|test|fix)/i,
|
|
6213
|
-
// Deferred: "left as", "deferred", "postponed", "out of scope"
|
|
6214
|
-
/\b(left\s+as|deferred|postponed|out\s+of\s+scope|for\s+later|in\s+a\s+future)/i,
|
|
6215
|
-
// Time-dependent: "after restart", "takes effect after", "once you"
|
|
6216
|
-
/\b(after\s+(restart|reboot|redeploy)|takes?\s+effect\s+after|once\s+you)/i,
|
|
6217
|
-
// === REMAINING WORK INDICATORS ===
|
|
6218
|
-
// "remaining tasks", "outstanding items"
|
|
6219
|
-
/\b(remaining|outstanding|pending|leftover)\s+(tasks?|items?|work|issues?|steps?)/i,
|
|
6220
|
-
// "X more to do", "still have to"
|
|
6221
|
-
/\b(more\s+to\s+do|still\s+have\s+to|yet\s+to\s+be\s+done)/i,
|
|
6222
|
-
// Explicit blockers
|
|
6223
|
-
/\b(blocker|blocked\s+by|waiting\s+(for|on)|depends?\s+on)/i,
|
|
6224
|
-
// === ERROR/FAILURE STATE ===
|
|
6225
|
-
// "failing tests", "build errors"
|
|
6226
|
-
/\b(failing|broken|erroring)\s+(tests?|builds?|checks?|validations?)/i,
|
|
6227
|
-
// "tests? (are )?(still )?failing"
|
|
6228
|
-
/\btests?\s+(are\s+)?(still\s+)?failing/i,
|
|
6229
|
-
// "errors? to (address|fix)"
|
|
6230
|
-
/\b(errors?|warnings?|issues?)\s+to\s+(address|fix|resolve)/i,
|
|
6231
|
-
// "doesn't work", "isn't working", "not working"
|
|
6232
|
-
/\b(doesn'?t|isn'?t|not)\s+(work|working|functional|functioning)/i,
|
|
6233
|
-
// === MANUAL STEPS REQUIRED ===
|
|
6234
|
-
// "you'll need to", "manually run", "requires user"
|
|
6235
|
-
/\b(you('ll|\s+will)\s+need\s+to|manually\s+(run|configure|set|update)|requires?\s+user)/i,
|
|
6236
|
-
// "run this command", "execute the following"
|
|
6237
|
-
/\b(run\s+this|execute\s+the\s+following|apply\s+the\s+migration)/i,
|
|
6238
|
-
// === TODO/FIXME IN PROSE ===
|
|
6239
|
-
// TODO or FIXME mentioned as remaining work (not in code blocks)
|
|
6240
|
-
/\b(todo|fixme|hack|xxx):\s/i,
|
|
6241
|
-
// "need to add", "should implement"
|
|
6242
|
-
/\b(need\s+to|should|must)\s+(add|implement|create|write|build|fix)\b/i,
|
|
6243
|
-
// === SCOPE LIMITATIONS ===
|
|
6244
|
-
// "didn't have time", "ran out of time"
|
|
6245
|
-
/\b(didn'?t|did\s+not)\s+have\s+(time|chance|opportunity)/i,
|
|
6246
|
-
// "beyond scope", "outside scope"
|
|
6247
|
-
/\b(beyond|outside)\s+(the\s+)?scope/i,
|
|
6248
|
-
// "for now" (temporary state)
|
|
6249
|
-
/\b(for\s+now|at\s+this\s+point|currently)\s*.{0,20}?(not|without|lacks?|missing)/i,
|
|
6250
|
-
];
|
|
6251
|
-
for (const pattern of incompletePatterns) {
|
|
6252
|
-
if (pattern.test(response)) {
|
|
6253
|
-
return true;
|
|
6254
|
-
}
|
|
6255
|
-
}
|
|
6256
|
-
return false;
|
|
6257
|
-
}
|
|
6258
6042
|
/**
|
|
6259
6043
|
* Extract tool names from a response by looking for tool call patterns
|
|
6260
6044
|
*/
|
|
@@ -6747,11 +6531,12 @@ Return ONLY JSON array:
|
|
|
6747
6531
|
const activity = normalized ? `Working: ${normalized}` : 'Working';
|
|
6748
6532
|
this.renderer?.setActivity(activity);
|
|
6749
6533
|
},
|
|
6750
|
-
onBeforeFirstToolCall: (toolNames) => {
|
|
6534
|
+
onBeforeFirstToolCall: (toolNames, _hasNarration) => {
|
|
6751
6535
|
const primaryTool = toolNames[0];
|
|
6752
6536
|
if (primaryTool) {
|
|
6753
6537
|
this.renderer?.setActivity(`Running ${primaryTool}`);
|
|
6754
6538
|
}
|
|
6539
|
+
// Don't inject synthetic thinking blocks - let the model respond naturally
|
|
6755
6540
|
return undefined;
|
|
6756
6541
|
},
|
|
6757
6542
|
onStreamChunk: (chunk, type) => {
|
|
@@ -6900,10 +6685,14 @@ Return ONLY JSON array:
|
|
|
6900
6685
|
activity = `Reading ${path}`;
|
|
6901
6686
|
}
|
|
6902
6687
|
this.renderer?.setActivity(activity);
|
|
6903
|
-
//
|
|
6688
|
+
// Estimate tokens for tool call (~50 tokens per call)
|
|
6689
|
+
this.streamingTokenCount += 50;
|
|
6690
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6904
6691
|
}
|
|
6905
6692
|
else {
|
|
6906
|
-
// Tool finished -
|
|
6693
|
+
// Tool finished - estimate result tokens (~100 per result)
|
|
6694
|
+
this.streamingTokenCount += 100;
|
|
6695
|
+
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6907
6696
|
// Reset to thinking state while model generates next response
|
|
6908
6697
|
this.renderer?.setActivity('Thinking');
|
|
6909
6698
|
}
|
|
@@ -6912,14 +6701,6 @@ Return ONLY JSON array:
|
|
|
6912
6701
|
this.lastAssistantResponse = response;
|
|
6913
6702
|
void this.runAutoQualityChecks('verification', response, context);
|
|
6914
6703
|
},
|
|
6915
|
-
// Real token usage from provider during streaming
|
|
6916
|
-
onUsage: (usage) => {
|
|
6917
|
-
const totalTokens = this.totalTokens(usage);
|
|
6918
|
-
if (totalTokens !== null) {
|
|
6919
|
-
this.streamingTokenCount = totalTokens;
|
|
6920
|
-
this.renderer?.updateStreamingTokens(this.streamingTokenCount);
|
|
6921
|
-
}
|
|
6922
|
-
},
|
|
6923
6704
|
// Retry notification for transient errors
|
|
6924
6705
|
onRetrying: (attempt, maxAttempts, error) => {
|
|
6925
6706
|
const shortError = error.message.slice(0, 100);
|