@trenchwork/erosolar 1.1.62 → 1.2.0
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/todoCapability.js +2 -2
- package/dist/capabilities/todoCapability.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/contracts/v1/agent.d.ts +12 -2
- package/dist/contracts/v1/agent.d.ts.map +1 -1
- package/dist/core/adversarialCorrection.d.ts +22 -0
- package/dist/core/adversarialCorrection.d.ts.map +1 -0
- package/dist/core/adversarialCorrection.js +25 -0
- package/dist/core/adversarialCorrection.js.map +1 -0
- package/dist/core/agent.d.ts +2 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +11 -45
- package/dist/core/agent.js.map +1 -1
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +5 -2
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/errorClassification.d.ts +44 -0
- package/dist/core/errorClassification.d.ts.map +1 -0
- package/dist/core/errorClassification.js +333 -0
- package/dist/core/errorClassification.js.map +1 -0
- package/dist/core/failureRegistry.d.ts +30 -0
- package/dist/core/failureRegistry.d.ts.map +1 -0
- package/dist/core/failureRegistry.js +74 -0
- package/dist/core/failureRegistry.js.map +1 -0
- package/dist/core/hitl.d.ts.map +1 -1
- package/dist/core/hitl.js +8 -0
- package/dist/core/hitl.js.map +1 -1
- package/dist/core/hostedAuth.d.ts +88 -0
- package/dist/core/hostedAuth.d.ts.map +1 -0
- package/dist/core/hostedAuth.js +219 -0
- package/dist/core/hostedAuth.js.map +1 -0
- package/dist/core/quota.d.ts +61 -0
- package/dist/core/quota.d.ts.map +1 -0
- package/dist/core/quota.js +104 -0
- package/dist/core/quota.js.map +1 -0
- package/dist/core/quotaErrors.d.ts.map +1 -1
- package/dist/core/quotaErrors.js +3 -5
- package/dist/core/quotaErrors.js.map +1 -1
- package/dist/core/resultVerification.d.ts +3 -2
- package/dist/core/resultVerification.d.ts.map +1 -1
- package/dist/core/resultVerification.js +3 -2
- package/dist/core/resultVerification.js.map +1 -1
- package/dist/core/secretStore.d.ts +11 -0
- package/dist/core/secretStore.d.ts.map +1 -1
- package/dist/core/secretStore.js +25 -0
- package/dist/core/secretStore.js.map +1 -1
- package/dist/core/slashCommands.d.ts.map +1 -1
- package/dist/core/slashCommands.js +4 -0
- package/dist/core/slashCommands.js.map +1 -1
- package/dist/core/thinkingVerbs.d.ts +31 -0
- package/dist/core/thinkingVerbs.d.ts.map +1 -0
- package/dist/core/thinkingVerbs.js +58 -0
- package/dist/core/thinkingVerbs.js.map +1 -0
- package/dist/core/turnGovernor.d.ts +63 -0
- package/dist/core/turnGovernor.d.ts.map +1 -0
- package/dist/core/turnGovernor.js +94 -0
- package/dist/core/turnGovernor.js.map +1 -0
- package/dist/core/updateChecker.d.ts.map +1 -1
- package/dist/core/updateChecker.js +5 -1
- package/dist/core/updateChecker.js.map +1 -1
- package/dist/core/usage.d.ts +28 -0
- package/dist/core/usage.d.ts.map +1 -0
- package/dist/core/usage.js +77 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/headless/interactiveShell.d.ts +6 -0
- package/dist/headless/interactiveShell.d.ts.map +1 -1
- package/dist/headless/interactiveShell.js +262 -42
- package/dist/headless/interactiveShell.js.map +1 -1
- package/dist/plugins/providers/deepseek/index.d.ts.map +1 -1
- package/dist/plugins/providers/deepseek/index.js +8 -5
- package/dist/plugins/providers/deepseek/index.js.map +1 -1
- package/dist/providers/baseProvider.d.ts +5 -13
- package/dist/providers/baseProvider.d.ts.map +1 -1
- package/dist/providers/baseProvider.js +12 -66
- package/dist/providers/baseProvider.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +27 -76
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/providers/resilientProvider.d.ts +2 -9
- package/dist/providers/resilientProvider.d.ts.map +1 -1
- package/dist/providers/resilientProvider.js +13 -199
- package/dist/providers/resilientProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +7 -0
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/shell/toolPresentation.d.ts +7 -0
- package/dist/shell/toolPresentation.d.ts.map +1 -1
- package/dist/shell/toolPresentation.js +78 -4
- package/dist/shell/toolPresentation.js.map +1 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +9 -3
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +10 -1
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/memoryTools.d.ts +7 -0
- package/dist/tools/memoryTools.d.ts.map +1 -1
- package/dist/tools/memoryTools.js +17 -0
- package/dist/tools/memoryTools.js.map +1 -1
- package/dist/tools/searchTools.d.ts.map +1 -1
- package/dist/tools/searchTools.js +5 -4
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/todoTools.d.ts +3 -4
- package/dist/tools/todoTools.d.ts.map +1 -1
- package/dist/tools/todoTools.js +23 -4
- package/dist/tools/todoTools.js.map +1 -1
- package/dist/tools/webTools.d.ts.map +1 -1
- package/dist/tools/webTools.js +3 -1
- package/dist/tools/webTools.js.map +1 -1
- package/dist/ui/ink/ChatStatic.d.ts.map +1 -1
- package/dist/ui/ink/ChatStatic.js +21 -5
- package/dist/ui/ink/ChatStatic.js.map +1 -1
- package/dist/ui/ink/InkPromptController.d.ts +5 -0
- package/dist/ui/ink/InkPromptController.d.ts.map +1 -1
- package/dist/ui/ink/InkPromptController.js +16 -6
- package/dist/ui/ink/InkPromptController.js.map +1 -1
- package/dist/ui/ink/Prompt.d.ts +6 -0
- package/dist/ui/ink/Prompt.d.ts.map +1 -1
- package/dist/ui/ink/Prompt.js +69 -10
- package/dist/ui/ink/Prompt.js.map +1 -1
- package/dist/ui/ink/StatusLine.d.ts +6 -0
- package/dist/ui/ink/StatusLine.d.ts.map +1 -1
- package/dist/ui/ink/StatusLine.js +17 -3
- package/dist/ui/ink/StatusLine.js.map +1 -1
- package/dist/ui/ink/pasteBuffer.d.ts +44 -0
- package/dist/ui/ink/pasteBuffer.d.ts.map +1 -0
- package/dist/ui/ink/pasteBuffer.js +73 -0
- package/dist/ui/ink/pasteBuffer.js.map +1 -0
- package/package.json +1 -1
- package/dist/core/index.d.ts +0 -7
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -7
- package/dist/core/index.js.map +0 -1
- package/dist/core/providerKeys.d.ts +0 -20
- package/dist/core/providerKeys.d.ts.map +0 -1
- package/dist/core/providerKeys.js +0 -40
- package/dist/core/providerKeys.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -49
- package/dist/plugins/index.d.ts.map +0 -1
- package/dist/plugins/index.js +0 -104
- package/dist/plugins/index.js.map +0 -1
- package/dist/plugins/tools/agentSpawning/agentSpawningPlugin.d.ts +0 -10
- package/dist/plugins/tools/agentSpawning/agentSpawningPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/agentSpawning/agentSpawningPlugin.js +0 -110
- package/dist/plugins/tools/agentSpawning/agentSpawningPlugin.js.map +0 -1
- package/dist/plugins/tools/bash/localBashPlugin.d.ts +0 -3
- package/dist/plugins/tools/bash/localBashPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/bash/localBashPlugin.js +0 -14
- package/dist/plugins/tools/bash/localBashPlugin.js.map +0 -1
- package/dist/plugins/tools/edit/editPlugin.d.ts +0 -9
- package/dist/plugins/tools/edit/editPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/edit/editPlugin.js +0 -15
- package/dist/plugins/tools/edit/editPlugin.js.map +0 -1
- package/dist/plugins/tools/enhancedGit/enhancedGitPlugin.d.ts +0 -3
- package/dist/plugins/tools/enhancedGit/enhancedGitPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/enhancedGit/enhancedGitPlugin.js +0 -9
- package/dist/plugins/tools/enhancedGit/enhancedGitPlugin.js.map +0 -1
- package/dist/plugins/tools/filesystem/localFilesystemPlugin.d.ts +0 -3
- package/dist/plugins/tools/filesystem/localFilesystemPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/filesystem/localFilesystemPlugin.js +0 -14
- package/dist/plugins/tools/filesystem/localFilesystemPlugin.js.map +0 -1
- package/dist/plugins/tools/gitHistory/gitHistoryPlugin.d.ts +0 -3
- package/dist/plugins/tools/gitHistory/gitHistoryPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/gitHistory/gitHistoryPlugin.js +0 -9
- package/dist/plugins/tools/gitHistory/gitHistoryPlugin.js.map +0 -1
- package/dist/plugins/tools/index.d.ts +0 -3
- package/dist/plugins/tools/index.d.ts.map +0 -1
- package/dist/plugins/tools/index.js +0 -3
- package/dist/plugins/tools/index.js.map +0 -1
- package/dist/plugins/tools/integrity/integrityPlugin.d.ts +0 -3
- package/dist/plugins/tools/integrity/integrityPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/integrity/integrityPlugin.js +0 -31
- package/dist/plugins/tools/integrity/integrityPlugin.js.map +0 -1
- package/dist/plugins/tools/mcp/mcpPlugin.d.ts +0 -3
- package/dist/plugins/tools/mcp/mcpPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/mcp/mcpPlugin.js +0 -27
- package/dist/plugins/tools/mcp/mcpPlugin.js.map +0 -1
- package/dist/plugins/tools/nodeDefaults.d.ts +0 -13
- package/dist/plugins/tools/nodeDefaults.d.ts.map +0 -1
- package/dist/plugins/tools/nodeDefaults.js +0 -33
- package/dist/plugins/tools/nodeDefaults.js.map +0 -1
- package/dist/plugins/tools/orchestration/orchestrationPlugin.d.ts +0 -3
- package/dist/plugins/tools/orchestration/orchestrationPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/orchestration/orchestrationPlugin.js +0 -340
- package/dist/plugins/tools/orchestration/orchestrationPlugin.js.map +0 -1
- package/dist/plugins/tools/registry.d.ts +0 -22
- package/dist/plugins/tools/registry.d.ts.map +0 -1
- package/dist/plugins/tools/registry.js +0 -58
- package/dist/plugins/tools/registry.js.map +0 -1
- package/dist/plugins/tools/search/localSearchPlugin.d.ts +0 -3
- package/dist/plugins/tools/search/localSearchPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/search/localSearchPlugin.js +0 -14
- package/dist/plugins/tools/search/localSearchPlugin.js.map +0 -1
- package/dist/plugins/tools/skills/skillPlugin.d.ts +0 -3
- package/dist/plugins/tools/skills/skillPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/skills/skillPlugin.js +0 -27
- package/dist/plugins/tools/skills/skillPlugin.js.map +0 -1
- package/dist/plugins/tools/todo/todoPlugin.d.ts +0 -3
- package/dist/plugins/tools/todo/todoPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/todo/todoPlugin.js +0 -10
- package/dist/plugins/tools/todo/todoPlugin.js.map +0 -1
- package/dist/runtime/agentWorkerPool.d.ts +0 -167
- package/dist/runtime/agentWorkerPool.d.ts.map +0 -1
- package/dist/runtime/agentWorkerPool.js +0 -435
- package/dist/runtime/agentWorkerPool.js.map +0 -1
- package/dist/shell/autoExecutor.d.ts +0 -70
- package/dist/shell/autoExecutor.d.ts.map +0 -1
- package/dist/shell/autoExecutor.js +0 -320
- package/dist/shell/autoExecutor.js.map +0 -1
- package/dist/shell/commandRegistry.d.ts +0 -122
- package/dist/shell/commandRegistry.d.ts.map +0 -1
- package/dist/shell/commandRegistry.js +0 -355
- package/dist/shell/commandRegistry.js.map +0 -1
- package/dist/shell/composableMessage.d.ts +0 -178
- package/dist/shell/composableMessage.d.ts.map +0 -1
- package/dist/shell/composableMessage.js +0 -384
- package/dist/shell/composableMessage.js.map +0 -1
- package/dist/shell/vimMode.d.ts +0 -66
- package/dist/shell/vimMode.d.ts.map +0 -1
- package/dist/shell/vimMode.js +0 -435
- package/dist/shell/vimMode.js.map +0 -1
- package/dist/tools/localExplore.d.ts +0 -38
- package/dist/tools/localExplore.d.ts.map +0 -1
- package/dist/tools/localExplore.js +0 -30
- package/dist/tools/localExplore.js.map +0 -1
|
@@ -13,9 +13,8 @@
|
|
|
13
13
|
* - Ctrl+C to interrupt
|
|
14
14
|
*/
|
|
15
15
|
import { stdin, stdout, exit } from 'node:process';
|
|
16
|
-
import { readFileSync
|
|
17
|
-
import { resolve, dirname,
|
|
18
|
-
import { homedir } from 'node:os';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { resolve, dirname, relative } from 'node:path';
|
|
19
18
|
import { fileURLToPath } from 'node:url';
|
|
20
19
|
import { exec as childExec } from 'node:child_process';
|
|
21
20
|
import { promisify } from 'node:util';
|
|
@@ -32,7 +31,10 @@ import { resolveProfileConfig } from '../config.js';
|
|
|
32
31
|
import { createAgentController } from '../runtime/agentController.js';
|
|
33
32
|
import { expandFileMentions, listWorkspaceFiles } from '../core/fileMentions.js';
|
|
34
33
|
import { resolveWorkspaceCaptureOptions, buildWorkspaceContext } from '../workspace.js';
|
|
35
|
-
import { loadAllSecrets, listSecretDefinitions, setSecretValue, getSecretValue } from '../core/secretStore.js';
|
|
34
|
+
import { loadAllSecrets, listSecretDefinitions, setSecretValue, getSecretValue, getSecretDefinition, classifyKeyEntry } from '../core/secretStore.js';
|
|
35
|
+
import { resolveKeyMode, keyModeLine, setPreferOwnKeys, clearHostedSession, loginViaLoopback } from '../core/hostedAuth.js';
|
|
36
|
+
import { appendMemoryNote } from '../tools/memoryTools.js';
|
|
37
|
+
import { recordDeepSeekUsage, getUsage, TAVILY_MONTHLY_FREE, TAVILY_ONE_TIME_BONUS } from '../core/usage.js';
|
|
36
38
|
import { listSessions, loadSessionById, saveSessionSnapshot } from '../core/sessionStore.js';
|
|
37
39
|
import { relativeTime } from '../core/relativeTime.js';
|
|
38
40
|
import { getModelContextInfo } from '../core/contextWindow.js';
|
|
@@ -48,6 +50,10 @@ import { setDebugMode, debugSnippet } from '../utils/debugLogger.js';
|
|
|
48
50
|
const exec = promisify(childExec);
|
|
49
51
|
import { ensureNextSteps } from '../core/finalResponseFormatter.js';
|
|
50
52
|
import { getTaskCompletionDetector, detectFailingTestOrBuild } from '../core/taskCompletionDetector.js';
|
|
53
|
+
import { TurnGovernor, pendingTodos, nextTodoPrompt } from '../core/turnGovernor.js';
|
|
54
|
+
import { FailureRegistry } from '../core/failureRegistry.js';
|
|
55
|
+
import { buildAdversarialCorrectionPrompt, MAX_ADVERSARIAL_CORRECTIONS } from '../core/adversarialCorrection.js';
|
|
56
|
+
import { getCurrentTodos } from '../tools/todoTools.js';
|
|
51
57
|
import { checkForUpdates, performBackgroundUpdate } from '../core/updateChecker.js';
|
|
52
58
|
import { startNewRun } from '../tools/fileChangeTracker.js';
|
|
53
59
|
import { onSudoPasswordNeeded, offSudoPasswordNeeded, provideSudoPassword } from '../core/sudoPasswordManager.js';
|
|
@@ -143,13 +149,20 @@ function getVersion() {
|
|
|
143
149
|
}
|
|
144
150
|
/** Inner content of the welcome box (plain, no border/colour). */
|
|
145
151
|
function welcomeBodyLines(input) {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
const title = input.version ? `✻ Welcome to Erosolar Coder ${input.version}` : '✻ Welcome to Erosolar Coder';
|
|
153
|
+
const body = [title, ''];
|
|
154
|
+
const mode = input.keyMode ?? (input.hasApiKey ? 'own' : 'none');
|
|
155
|
+
if (mode === 'hosted') {
|
|
156
|
+
// Signed in — running on hosted keys. The mode line names the account so
|
|
157
|
+
// it's unmistakable this is NOT the user's own key.
|
|
158
|
+
body.push(input.keyModeLine ?? 'Signed in · using hosted keys');
|
|
149
159
|
}
|
|
150
|
-
else {
|
|
160
|
+
else if (mode === 'own') {
|
|
151
161
|
body.push(`${input.model} · ${input.provider}`, `Key: ${input.maskedKey} · /help for commands`);
|
|
152
162
|
}
|
|
163
|
+
else {
|
|
164
|
+
body.push('⚠ No DeepSeek API key configured', '', '/login Sign in with Google for hosted keys', '', 'Or bring your own:', ' /key sk-… DeepSeek (required) · platform.deepseek.com', ' /key tvly-… Tavily web search (optional) · tavily.com');
|
|
165
|
+
}
|
|
153
166
|
if (input.cwd)
|
|
154
167
|
body.push(`cwd: ${input.cwd}`);
|
|
155
168
|
return body;
|
|
@@ -272,9 +285,26 @@ class InteractiveShell {
|
|
|
272
285
|
};
|
|
273
286
|
pendingModelSwitch = null;
|
|
274
287
|
currentResponseBuffer = '';
|
|
288
|
+
// The turn's final assistant text, captured BEFORE currentResponseBuffer is
|
|
289
|
+
// cleared on message.complete. The auto-continue refusal/completion/governor
|
|
290
|
+
// reads run in the `finally`, AFTER that clear, so reading the buffer there saw
|
|
291
|
+
// '' and blinded them (completion detection + safety-refusal both need the
|
|
292
|
+
// text). This mirrors the buffer's content but is never cleared mid-turn.
|
|
293
|
+
finalResponseText = '';
|
|
275
294
|
// Store original prompt for auto-continuation
|
|
276
295
|
originalPromptForAutoContinue = null;
|
|
277
296
|
// (Pinned prompt removed per request — field intentionally absent.)
|
|
297
|
+
// Bounds + stall-detects the auto-continue loop per user request, and drives
|
|
298
|
+
// continuation from the live TODO plan (see src/core/turnGovernor.ts). Reset
|
|
299
|
+
// when a fresh user prompt arrives.
|
|
300
|
+
autoGovernor = new TurnGovernor();
|
|
301
|
+
// Remembers recurring error signatures across auto-continue turns so the
|
|
302
|
+
// agent stops re-trying the same dead end (see src/core/failureRegistry.ts).
|
|
303
|
+
failureRegistry = new FailureRegistry();
|
|
304
|
+
// Adversarial auto-correction: how many bounded re-fixes the reviewer has
|
|
305
|
+
// triggered for the CURRENT user request (capped). Reset on a fresh prompt;
|
|
306
|
+
// the findings themselves are a per-turn local in processPrompt.
|
|
307
|
+
adversarialCorrectionCount = 0;
|
|
278
308
|
constructor(controller, profile, profileConfig, workingDir) {
|
|
279
309
|
this.controller = controller;
|
|
280
310
|
this.profile = profile;
|
|
@@ -345,6 +375,8 @@ class InteractiveShell {
|
|
|
345
375
|
// Esc interrupts a running turn (handleInterrupt no-ops when idle), so
|
|
346
376
|
// the spinner's "esc to interrupt" is real. Ctrl+C still works too.
|
|
347
377
|
onEscape: () => this.handleInterrupt(),
|
|
378
|
+
onShowShortcuts: () => this.showKeyboardShortcuts(),
|
|
379
|
+
onDismissPanel: () => this.dismissInlinePanel(),
|
|
348
380
|
});
|
|
349
381
|
// Register cleanup callback for graceful shutdown
|
|
350
382
|
onShutdown(() => {
|
|
@@ -492,12 +524,16 @@ class InteractiveShell {
|
|
|
492
524
|
// WHICH lines appear; here we draw the same box with brand colour.
|
|
493
525
|
const flare = chalk.hex('#ff6a1f');
|
|
494
526
|
const wire = chalk.hex('#3a362e');
|
|
527
|
+
const keyStatus = resolveKeyMode();
|
|
495
528
|
const body = welcomeBodyLines({
|
|
496
529
|
hasApiKey,
|
|
497
530
|
maskedKey: hasApiKey ? maskApiKey(apiKey) : '',
|
|
498
531
|
model: this.profileConfig.model,
|
|
499
532
|
provider: this.profileConfig.provider,
|
|
500
533
|
cwd: this.workingDir,
|
|
534
|
+
keyMode: keyStatus.mode,
|
|
535
|
+
keyModeLine: keyModeLine(keyStatus),
|
|
536
|
+
version: `v${version}`,
|
|
501
537
|
});
|
|
502
538
|
const boxed = roundedBox(body, (cell) => cell.replace('✻', flare('✻')), (s) => wire(s));
|
|
503
539
|
const welcomeContent = ['', ...updateLines, ...boxed, ''].join('\n');
|
|
@@ -791,26 +827,19 @@ class InteractiveShell {
|
|
|
791
827
|
const lower = trimmed.toLowerCase();
|
|
792
828
|
// /model and /secrets were removed: Erosolar is locked to deepseek-v4-pro
|
|
793
829
|
// on max thought (no model switching), and /key is the one key you set.
|
|
794
|
-
// Handle /key
|
|
830
|
+
// Handle /key — set your own DeepSeek OR Tavily API key. Routed by prefix:
|
|
831
|
+
// `sk-…` → DeepSeek (the model), `tvly-…` → Tavily (web search). Explicit
|
|
832
|
+
// `/key tavily <k>` / `/key deepseek <k>` also work. Bring-your-own-key is
|
|
833
|
+
// the model; both are stored in the OS-permission secret store.
|
|
795
834
|
if (lower === '/key' || lower.startsWith('/key ')) {
|
|
796
|
-
const parts = trimmed.split(/\s+/);
|
|
797
|
-
const keyValue = parts[1];
|
|
798
835
|
const renderer = this.promptController?.getRenderer();
|
|
799
|
-
|
|
800
|
-
|
|
836
|
+
const arg = trimmed.slice('/key'.length).trim();
|
|
837
|
+
const entry = classifyKeyEntry(arg);
|
|
838
|
+
if (entry) {
|
|
801
839
|
try {
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
const existing = existsSync(secretFile)
|
|
806
|
-
? JSON.parse(readFileSync(secretFile, 'utf-8'))
|
|
807
|
-
: {};
|
|
808
|
-
existing['DEEPSEEK_API_KEY'] = keyValue;
|
|
809
|
-
writeFileSync(secretFile, JSON.stringify(existing, null, 2) + '\n');
|
|
810
|
-
// Also set in process.env for immediate use
|
|
811
|
-
process.env['DEEPSEEK_API_KEY'] = keyValue;
|
|
812
|
-
// Show confirmation via renderer
|
|
813
|
-
renderer?.addEvent('system', chalk.green('✓ DEEPSEEK_API_KEY saved'));
|
|
840
|
+
setSecretValue(entry.id, entry.value);
|
|
841
|
+
const label = getSecretDefinition(entry.id)?.label ?? entry.id;
|
|
842
|
+
renderer?.addEvent('system', chalk.green(`✓ ${label} saved`));
|
|
814
843
|
}
|
|
815
844
|
catch (error) {
|
|
816
845
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -818,11 +847,38 @@ class InteractiveShell {
|
|
|
818
847
|
}
|
|
819
848
|
}
|
|
820
849
|
else {
|
|
821
|
-
|
|
822
|
-
renderer?.addEvent('system', chalk.yellow('Usage: /key YOUR_API_KEY'));
|
|
850
|
+
renderer?.addEvent('system', chalk.yellow('Usage: /key sk-… (DeepSeek) or /key tvly-… (Tavily web search)'));
|
|
823
851
|
}
|
|
824
852
|
return true;
|
|
825
853
|
}
|
|
854
|
+
// /account — show the active key source (hosted vs your own) and switch
|
|
855
|
+
// between them. `/account own` forces your own keys even while signed in;
|
|
856
|
+
// `/account hosted` returns to hosted. Hosted keys come from sign-in
|
|
857
|
+
// (server-side, never baked into this client) — see core/hostedAuth.ts.
|
|
858
|
+
if (lower === '/account' || lower.startsWith('/account ')) {
|
|
859
|
+
const r = this.promptController?.getRenderer();
|
|
860
|
+
const arg = trimmed.slice('/account'.length).trim().toLowerCase();
|
|
861
|
+
if (arg === 'own')
|
|
862
|
+
setPreferOwnKeys(true);
|
|
863
|
+
else if (arg === 'hosted')
|
|
864
|
+
setPreferOwnKeys(false);
|
|
865
|
+
if (arg === 'own' || arg === 'hosted')
|
|
866
|
+
void this.showWelcome(); // banner reflects the switch
|
|
867
|
+
r?.addEvent('system', this.accountStatusText(resolveKeyMode()));
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
// /login — Google sign-in via ero.solar (loopback OAuth) to unlock hosted keys.
|
|
871
|
+
if (lower === '/login' || lower === '/signin') {
|
|
872
|
+
void this.handleLogin();
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
// /logout — drop the hosted session (back to your own keys, or none).
|
|
876
|
+
if (lower === '/logout' || lower === '/signout') {
|
|
877
|
+
clearHostedSession();
|
|
878
|
+
this.promptController?.getRenderer()?.addEvent('system', chalk.green('✓ Signed out — using your own keys.'));
|
|
879
|
+
void this.showWelcome();
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
826
882
|
// /update — check npm for a newer version and upgrade in-shell.
|
|
827
883
|
if (lower === '/update' || lower === '/upgrade') {
|
|
828
884
|
void this.handleUpdateCommand();
|
|
@@ -866,6 +922,13 @@ class InteractiveShell {
|
|
|
866
922
|
this.showContext();
|
|
867
923
|
return true;
|
|
868
924
|
}
|
|
925
|
+
// /cost — DeepSeek tokens + Tavily searches consumed (this session + all
|
|
926
|
+
// time), and the hosted free-pool reference. Account-wide remaining is a
|
|
927
|
+
// backend number shown in the ero.solar portal.
|
|
928
|
+
if (lower === '/cost' || lower === '/spend') {
|
|
929
|
+
this.showUsage();
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
869
932
|
// /diff — review the files the agent changed this run, as colored diffs.
|
|
870
933
|
if (lower === '/diff' || lower === '/changes') {
|
|
871
934
|
this.showDiff();
|
|
@@ -1406,6 +1469,29 @@ class InteractiveShell {
|
|
|
1406
1469
|
this.promptController.setInlinePanel(lines);
|
|
1407
1470
|
this.scheduleInlinePanelDismiss();
|
|
1408
1471
|
}
|
|
1472
|
+
/** /cost — DeepSeek tokens + Tavily searches consumed (this install). */
|
|
1473
|
+
showUsage() {
|
|
1474
|
+
if (!this.promptController?.supportsInlinePanel()) {
|
|
1475
|
+
this.promptController?.setStatusMessage('Use /cost in interactive mode');
|
|
1476
|
+
setTimeout(() => this.promptController?.setStatusMessage(null), 3000);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const { session, cumulative } = getUsage();
|
|
1480
|
+
const label = (s) => chalk.hex('#ffb142')(s.padEnd(9));
|
|
1481
|
+
const dim = (s) => chalk.dim(s);
|
|
1482
|
+
const ds = (u) => `${formatTokenCount(u.deepseekInputTokens)} in · ${formatTokenCount(u.deepseekOutputTokens)} out`;
|
|
1483
|
+
const lines = [
|
|
1484
|
+
chalk.bold.hex('#ece6da')('Usage') + dim(' (press any key to dismiss)'),
|
|
1485
|
+
'',
|
|
1486
|
+
label('DeepSeek') + dim(`${ds(cumulative)} · this session ${ds(session)}`),
|
|
1487
|
+
label('Tavily') + dim(`${cumulative.tavilySearches} searches · this session ${session.tavilySearches}`),
|
|
1488
|
+
'',
|
|
1489
|
+
dim(`Hosted free pool: Tavily ${TAVILY_MONTHLY_FREE.toLocaleString('en-US')}/mo + ${TAVILY_ONE_TIME_BONUS.toLocaleString('en-US')} one-time bonus.`),
|
|
1490
|
+
dim('Account-wide totals + remaining show in the ero.solar portal after sign-in.'),
|
|
1491
|
+
];
|
|
1492
|
+
this.promptController.setInlinePanel(lines);
|
|
1493
|
+
this.scheduleInlinePanelDismiss();
|
|
1494
|
+
}
|
|
1409
1495
|
/**
|
|
1410
1496
|
* /diff — review every file the agent changed this run as a colored diff,
|
|
1411
1497
|
* in a dismissable panel. Reads each file's original content from the change
|
|
@@ -1489,6 +1575,54 @@ class InteractiveShell {
|
|
|
1489
1575
|
revertAllChanges(this.workingDir); // restores/deletes on disk + clears tracking
|
|
1490
1576
|
renderer?.addEvent('system', chalk.green('✓ ' + rewindResultLine(restored, deleted)));
|
|
1491
1577
|
}
|
|
1578
|
+
/** One-line summary of the active key source for /account. */
|
|
1579
|
+
accountStatusText(s) {
|
|
1580
|
+
if (s.mode === 'hosted') {
|
|
1581
|
+
return chalk.green(`Hosted keys · signed in as ${s.email}.`) +
|
|
1582
|
+
chalk.dim(` /account own to use your own · /logout to sign out.`);
|
|
1583
|
+
}
|
|
1584
|
+
if (s.mode === 'own') {
|
|
1585
|
+
return chalk.green(`Your own keys · DeepSeek${s.ownTavily ? ' + Tavily' : ''}.`) +
|
|
1586
|
+
chalk.dim(s.signedIn ? ` /account hosted to use hosted keys.` : ` /login to use hosted keys.`);
|
|
1587
|
+
}
|
|
1588
|
+
return chalk.yellow('No keys configured.') +
|
|
1589
|
+
chalk.dim(' /login for hosted keys, or set your own: /key sk-… (and /key tvly-…).');
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* /login — Google sign-in via ero.solar. Opens the browser to the SSO URL and
|
|
1593
|
+
* runs a one-shot 127.0.0.1 loopback server that captures the redirect with
|
|
1594
|
+
* the short-lived token (see core/hostedAuth.ts). On success the CLI is on
|
|
1595
|
+
* hosted keys; no key ever touches this client.
|
|
1596
|
+
*/
|
|
1597
|
+
async handleLogin() {
|
|
1598
|
+
const r = this.promptController?.getRenderer();
|
|
1599
|
+
const status = resolveKeyMode();
|
|
1600
|
+
if (status.signedIn) {
|
|
1601
|
+
r?.addEvent('system', chalk.green(`Already signed in as ${status.email}.`) +
|
|
1602
|
+
chalk.dim(' /logout to sign out · /account to switch key source.'));
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
r?.addEvent('system', chalk.dim('Opening ero.solar sign-in in your browser — finish there, then return here…'));
|
|
1606
|
+
const result = await loginViaLoopback({ open: (url) => this.openInBrowser(url) });
|
|
1607
|
+
if (result.ok && result.session) {
|
|
1608
|
+
r?.addEvent('system', chalk.green(`✓ Signed in as ${result.session.email} — using hosted keys.`));
|
|
1609
|
+
void this.showWelcome();
|
|
1610
|
+
}
|
|
1611
|
+
else {
|
|
1612
|
+
r?.addEvent('system', chalk.yellow(`Sign-in didn't complete: ${result.error ?? 'unknown error'}.`) +
|
|
1613
|
+
chalk.dim(' Retry /login, or use /key sk-… for your own key.'));
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
/** Best-effort open a URL in the OS browser; also prints it as a fallback. */
|
|
1617
|
+
openInBrowser(url) {
|
|
1618
|
+
const opener = process.platform === 'darwin' ? 'open'
|
|
1619
|
+
: process.platform === 'win32' ? 'start ""'
|
|
1620
|
+
: 'xdg-open';
|
|
1621
|
+
// url is built by loginViaLoopback (no user input) and JSON-quoted, so the
|
|
1622
|
+
// `&` in the query string can't break out of the argument.
|
|
1623
|
+
childExec(`${opener} ${JSON.stringify(url)}`, () => { });
|
|
1624
|
+
this.promptController?.getRenderer()?.addEvent('system', chalk.dim(`If the browser didn't open: ${url}`));
|
|
1625
|
+
}
|
|
1492
1626
|
showHelp() {
|
|
1493
1627
|
if (!this.promptController?.supportsInlinePanel()) {
|
|
1494
1628
|
this.promptController?.setStatusMessage('Help: /key sk-… (everything else is automatic)');
|
|
@@ -1503,14 +1637,20 @@ class InteractiveShell {
|
|
|
1503
1637
|
const lines = [
|
|
1504
1638
|
chalk.bold.hex('#ece6da')('Erosolar Coder') + dim(' (press any key to dismiss)'),
|
|
1505
1639
|
'',
|
|
1506
|
-
cmd('/
|
|
1640
|
+
cmd('/login') + dim(' Sign in with Google (ero.solar) to use hosted keys'),
|
|
1641
|
+
cmd('/key sk-…') + dim(' Set your DeepSeek API key (required)'),
|
|
1642
|
+
cmd('/key tvly-…') + dim(' Set your Tavily key for web search (optional)'),
|
|
1643
|
+
cmd('/account') + dim(' Show / switch key source (hosted vs your own)'),
|
|
1507
1644
|
cmd('/update') + dim(' Check npm and upgrade to the latest version'),
|
|
1508
1645
|
cmd('/resume') + dim(' Restore a previous conversation'),
|
|
1509
1646
|
cmd('/context') + dim(' Show context-window usage'),
|
|
1647
|
+
cmd('/cost') + dim(' DeepSeek tokens + Tavily searches consumed'),
|
|
1510
1648
|
cmd('/diff') + dim(' Review changes made this run'),
|
|
1511
1649
|
cmd('/rewind') + dim(' Undo this run\'s file changes'),
|
|
1512
1650
|
'',
|
|
1513
|
-
dim('
|
|
1651
|
+
dim('Prefixes: ') + cmd('@file') + dim(' attach · ') + cmd('!cmd') + dim(' run shell · ') + cmd('#note') + dim(' save to memory'),
|
|
1652
|
+
'',
|
|
1653
|
+
dim('Everything else runs automatically —'),
|
|
1514
1654
|
dim('deepseek-v4-pro · max thought · ultracode · adversarial verifier, all on.'),
|
|
1515
1655
|
dim('Shift+Tab cycles permission mode · Ctrl+D exits · ? for shortcuts'),
|
|
1516
1656
|
];
|
|
@@ -1609,6 +1749,19 @@ class InteractiveShell {
|
|
|
1609
1749
|
void this.runLocalCommand(trimmed.slice(1).trim());
|
|
1610
1750
|
return;
|
|
1611
1751
|
}
|
|
1752
|
+
// `#note` — quick-capture a note to persistent project memory (Claude Code
|
|
1753
|
+
// parity), no model round-trip. Lands in .erosolar/memory/ where the agent
|
|
1754
|
+
// reads it on later sessions.
|
|
1755
|
+
if (trimmed.startsWith('#')) {
|
|
1756
|
+
this.dismissInlinePanel();
|
|
1757
|
+
const note = trimmed.slice(1).trim();
|
|
1758
|
+
const r = this.promptController?.getRenderer();
|
|
1759
|
+
if (appendMemoryNote(this.workingDir, note))
|
|
1760
|
+
r?.addEvent('system', chalk.green('✓ Saved to memory'));
|
|
1761
|
+
else
|
|
1762
|
+
r?.addEvent('system', chalk.yellow('Usage: #<note to remember>'));
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1612
1765
|
// Dismiss inline panel for regular user prompts
|
|
1613
1766
|
this.dismissInlinePanel();
|
|
1614
1767
|
// Live follow-up queue (Claude Code parity): a prompt typed while the agent
|
|
@@ -1645,18 +1798,29 @@ class InteractiveShell {
|
|
|
1645
1798
|
// A fresh user prompt clears any prior interrupt state — this is new
|
|
1646
1799
|
// work the user actually wants done.
|
|
1647
1800
|
this.userInterruptedRun = false;
|
|
1801
|
+
// Fresh user request → start a new auto-continue turn budget + failure log.
|
|
1802
|
+
this.autoGovernor.reset();
|
|
1803
|
+
this.failureRegistry.reset();
|
|
1804
|
+
this.adversarialCorrectionCount = 0;
|
|
1648
1805
|
// Pinned-prompt persistence removed per request — no longer
|
|
1649
1806
|
// displayed above the chat box.
|
|
1650
1807
|
}
|
|
1651
1808
|
enterCriticalSection();
|
|
1652
1809
|
this.isProcessing = true;
|
|
1653
1810
|
this.currentResponseBuffer = '';
|
|
1811
|
+
this.finalResponseText = '';
|
|
1654
1812
|
this.promptController?.setStreaming(true);
|
|
1655
1813
|
this.promptController?.setStatusMessage('Analyzing request…');
|
|
1656
1814
|
const renderer = this.promptController?.getRenderer();
|
|
1657
1815
|
let episodeSuccess = false;
|
|
1658
1816
|
const toolsUsed = [];
|
|
1659
1817
|
const filesModified = [];
|
|
1818
|
+
// Tail of this turn's tool outputs (where TS/test/build errors land), so the
|
|
1819
|
+
// failure registry + governor see real error text, not just the narration.
|
|
1820
|
+
let turnToolOutput = '';
|
|
1821
|
+
// Reviewer findings from THIS turn (set by the adversarial.findings event),
|
|
1822
|
+
// used in the finally to drive a bounded auto-correction.
|
|
1823
|
+
let turnAdversarialFindings = null;
|
|
1660
1824
|
// Track reasoning content for fallback when response is empty
|
|
1661
1825
|
let reasoningBuffer = '';
|
|
1662
1826
|
// Track reasoning-only time to prevent models from reasoning forever without action
|
|
@@ -1713,6 +1877,7 @@ class InteractiveShell {
|
|
|
1713
1877
|
case 'message.start':
|
|
1714
1878
|
// AI has started processing - update status to show activity
|
|
1715
1879
|
this.currentResponseBuffer = '';
|
|
1880
|
+
this.finalResponseText = '';
|
|
1716
1881
|
reasoningBuffer = '';
|
|
1717
1882
|
reasoningOnlyStartTime = null; // Reset on new message
|
|
1718
1883
|
this.promptController?.setStatusMessage('Thinking...');
|
|
@@ -1720,6 +1885,7 @@ class InteractiveShell {
|
|
|
1720
1885
|
case 'message.delta':
|
|
1721
1886
|
// Stream content as it arrives
|
|
1722
1887
|
this.currentResponseBuffer += event.content ?? '';
|
|
1888
|
+
this.finalResponseText += event.content ?? '';
|
|
1723
1889
|
if (renderer) {
|
|
1724
1890
|
renderer.addEvent('stream', event.content);
|
|
1725
1891
|
}
|
|
@@ -1784,6 +1950,9 @@ class InteractiveShell {
|
|
|
1784
1950
|
}
|
|
1785
1951
|
}
|
|
1786
1952
|
renderer.addEvent('response', '\n');
|
|
1953
|
+
// Capture the authoritative final text BEFORE the buffer is cleared
|
|
1954
|
+
// (the finally's auto-continue reads run after this clear).
|
|
1955
|
+
this.finalResponseText = sourceText || this.finalResponseText;
|
|
1787
1956
|
}
|
|
1788
1957
|
this.currentResponseBuffer = '';
|
|
1789
1958
|
break;
|
|
@@ -1820,6 +1989,11 @@ class InteractiveShell {
|
|
|
1820
1989
|
if (isHitlToolName(event.toolName)) {
|
|
1821
1990
|
hitlDepth = Math.max(0, hitlDepth - 1);
|
|
1822
1991
|
}
|
|
1992
|
+
// Keep the tail of tool output for the failure registry / governor
|
|
1993
|
+
// (errors land here, not in the assistant narration).
|
|
1994
|
+
if (typeof event.result === 'string' && event.result) {
|
|
1995
|
+
turnToolOutput = (turnToolOutput + '\n' + event.result).slice(-16000);
|
|
1996
|
+
}
|
|
1823
1997
|
// Clear the activity label; the agent is thinking again.
|
|
1824
1998
|
this.promptController?.setStatusMessage('Thinking…');
|
|
1825
1999
|
// Reset reasoning timer after tool completes
|
|
@@ -1857,6 +2031,8 @@ class InteractiveShell {
|
|
|
1857
2031
|
}
|
|
1858
2032
|
break;
|
|
1859
2033
|
case 'usage': {
|
|
2034
|
+
// Meter cumulative DeepSeek consumption for /usage + the portal.
|
|
2035
|
+
recordDeepSeekUsage(event.inputTokens, event.outputTokens);
|
|
1860
2036
|
// inputTokens = exactly what occupies the context window this turn.
|
|
1861
2037
|
// The real model window (not a hardcoded guess) is the denominator
|
|
1862
2038
|
// so "% context left" reflects the actual model.
|
|
@@ -1883,6 +2059,11 @@ class InteractiveShell {
|
|
|
1883
2059
|
elapsedMs: event.elapsedMs,
|
|
1884
2060
|
})));
|
|
1885
2061
|
break;
|
|
2062
|
+
case 'adversarial.findings':
|
|
2063
|
+
// The reviewer refuted this turn's draft — remember it so the
|
|
2064
|
+
// auto-continue loop can run a bounded re-fix (handled in finally).
|
|
2065
|
+
turnAdversarialFindings = event.findings;
|
|
2066
|
+
break;
|
|
1886
2067
|
case 'context.compacted': {
|
|
1887
2068
|
// The conversation was auto-compacted to stay within the window —
|
|
1888
2069
|
// surface it as a dim note (Claude Code parity) instead of silently.
|
|
@@ -2007,7 +2188,7 @@ class InteractiveShell {
|
|
|
2007
2188
|
// model declines the request, the request is *done* — auto-continue
|
|
2008
2189
|
// would just resubmit "continue" and start a new spinner cycle, which
|
|
2009
2190
|
// is what produced the stuck "Thinking… (4m N s)" timer the user saw.
|
|
2010
|
-
const refusedTurn = isSafetyRefusal(this.
|
|
2191
|
+
const refusedTurn = isSafetyRefusal(this.finalResponseText);
|
|
2011
2192
|
this.isProcessing = false;
|
|
2012
2193
|
this.promptController?.setStreaming(false);
|
|
2013
2194
|
this.promptController?.setStatusMessage(null);
|
|
@@ -2028,6 +2209,10 @@ class InteractiveShell {
|
|
|
2028
2209
|
r?.setQueuedPrompts([]);
|
|
2029
2210
|
// Note: pendingPrompts may still have items if a drain just started
|
|
2030
2211
|
// a new processPrompt; the new run will manage the list.
|
|
2212
|
+
// Snapshot this turn's full output (tool results + narration) BEFORE the
|
|
2213
|
+
// buffer is cleared — the auto-continue governor + failure registry need
|
|
2214
|
+
// the real error text, which the reset below would otherwise wipe.
|
|
2215
|
+
const combinedTurnOutput = (turnToolOutput + '\n' + this.finalResponseText).slice(-16000);
|
|
2031
2216
|
this.currentResponseBuffer = '';
|
|
2032
2217
|
// Autosave the conversation so /resume has something to restore. Each
|
|
2033
2218
|
// turn updates the same snapshot in place (keyed by this.sessionId).
|
|
@@ -2059,20 +2244,55 @@ class InteractiveShell {
|
|
|
2059
2244
|
if (autoMode !== 'off') {
|
|
2060
2245
|
// Check if original user prompt is fully completed
|
|
2061
2246
|
const detector = getTaskCompletionDetector();
|
|
2062
|
-
const analysis = detector.analyzeCompletion(this.
|
|
2063
|
-
//
|
|
2064
|
-
|
|
2247
|
+
const analysis = detector.analyzeCompletion(this.finalResponseText, toolsUsed);
|
|
2248
|
+
// Record this turn with the governor (bounds the loop + detects a
|
|
2249
|
+
// stall: the same tools/files/failure repeating with no new progress)
|
|
2250
|
+
// and the failure registry (catches the same error recurring across
|
|
2251
|
+
// NON-consecutive turns — a thrash the stall check would miss).
|
|
2252
|
+
this.autoGovernor.recordTurn({
|
|
2253
|
+
toolsUsed,
|
|
2254
|
+
filesModified,
|
|
2255
|
+
failingSignal: detectFailingTestOrBuild(combinedTurnOutput),
|
|
2256
|
+
});
|
|
2257
|
+
this.failureRegistry.trackTurn(combinedTurnOutput);
|
|
2258
|
+
const gov = this.autoGovernor.check();
|
|
2259
|
+
const failureNudge = this.failureRegistry.nudge();
|
|
2260
|
+
const todos = getCurrentTodos();
|
|
2261
|
+
const pending = pendingTodos(todos);
|
|
2262
|
+
if (gov.stop) {
|
|
2263
|
+
// Yield to the user WITH state instead of thrashing forever.
|
|
2264
|
+
const note = gov.reason === 'limit'
|
|
2265
|
+
? `Paused after ${gov.turn} auto-continue turns (turn limit).${pending.length ? ` ${pending.length} task${pending.length === 1 ? '' : 's'} still pending` : ''} — say "continue" to keep going.`
|
|
2266
|
+
: `Paused: no new progress over the last few turns (same actions repeating).${pending.length ? ` ${pending.length} task${pending.length === 1 ? '' : 's'} pending` : ''} — tell me how to proceed.`;
|
|
2267
|
+
this.promptController?.getRenderer()?.addEvent('system', chalk.dim(note));
|
|
2268
|
+
this.promptController?.setStatusMessage(null);
|
|
2269
|
+
this.originalPromptForAutoContinue = null;
|
|
2270
|
+
}
|
|
2271
|
+
else if (turnAdversarialFindings && this.adversarialCorrectionCount < MAX_ADVERSARIAL_CORRECTIONS) {
|
|
2272
|
+
// The reviewer refuted this turn's draft — re-run the FULL tool loop
|
|
2273
|
+
// to actually fix the findings (not just show the caveat), bounded
|
|
2274
|
+
// by the governor + this per-request cap.
|
|
2275
|
+
this.adversarialCorrectionCount += 1;
|
|
2276
|
+
this.promptController?.setStatusMessage('Addressing reviewer findings…');
|
|
2277
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
2278
|
+
await this.processPrompt(buildAdversarialCorrectionPrompt(turnAdversarialFindings));
|
|
2279
|
+
}
|
|
2280
|
+
else if (!analysis.isComplete || pending.length > 0) {
|
|
2281
|
+
// Continue — but only stop when the LIVE PLAN is also clear: pending
|
|
2282
|
+
// todos force a continue even if the response sounded "done".
|
|
2065
2283
|
this.promptController?.setStatusMessage('Continuing...');
|
|
2066
2284
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2067
|
-
//
|
|
2068
|
-
const
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2285
|
+
// Prefer the plan's next task; fall back to the response heuristic.
|
|
2286
|
+
const base = nextTodoPrompt(todos)
|
|
2287
|
+
?? this.generateAutoContinuePrompt(this.originalPromptForAutoContinue || '', combinedTurnOutput, toolsUsed)
|
|
2288
|
+
?? 'continue';
|
|
2289
|
+
// When a failure keeps recurring, lead with the change-approach nudge.
|
|
2290
|
+
// Keep an IMPORTANT: prefix so this counts as an auto-continue (not a
|
|
2291
|
+
// fresh user prompt, which would reset the governor).
|
|
2292
|
+
const autoPrompt = failureNudge
|
|
2293
|
+
? `IMPORTANT: ${failureNudge}\n\n${base.replace(/^IMPORTANT:\s*/, '')}`
|
|
2294
|
+
: base;
|
|
2295
|
+
await this.processPrompt(autoPrompt);
|
|
2076
2296
|
}
|
|
2077
2297
|
else {
|
|
2078
2298
|
this.promptController?.setStatusMessage('Task complete');
|