vigthoria-cli 1.11.25 → 1.11.28

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.
@@ -171,6 +171,11 @@ export declare class ChatCommand {
171
171
  private runOperatorDirectAnswer;
172
172
  private runSimplePrompt;
173
173
  private runAgentTurn;
174
+ private resolveAgentTurnPrompt;
175
+ private shouldPreserveBuildOutcomeForContinue;
176
+ private resolveOriginalPrompt;
177
+ private commitAgentRunOutcome;
178
+ private printResumeContinuationHint;
174
179
  private buildLocalLoopLiveOutcome;
175
180
  private persistLocalLoopOutcome;
176
181
  private runLocalAgentLoop;
@@ -17,32 +17,29 @@ import { runAgentSessionMenu, shouldShowAgentSessionMenu } from './agent-session
17
17
  import { createLiveOutcome, evaluateExecutorSuccess, handleTaskEvent, isSubstantiveAgentAnswer, isToolEvidenceStubAnswer, normalizeAgentAnswerContent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
18
18
  import { looksLikeMarkdownReport, renderMarkdownToTerminal, summarizeMarkdownReport, } from '../utils/terminalMarkdown.js';
19
19
  import { emitDeckEvent, isDeckModeEnabled } from '../utils/deckEvents.js';
20
- import { inferAgentTaskType as sharedInferAgentTaskType, isTrivialHtmlPageRequest, resolvePlannerAgentTimeoutMs, resolveWorkflowType, shouldSkipAnalysisRescue, taskRequiresWorkspaceChanges as promptRequiresWorkspaceChanges, buildExecutionHints, } from '../utils/requestIntent.js';
20
+ import { inferAgentTaskType as sharedInferAgentTaskType, isTrivialHtmlPageRequest, resolvePlannerAgentTimeoutMs, resolveWorkflowType, shouldSkipAnalysisRescue, taskRequiresWorkspaceChanges as promptRequiresWorkspaceChanges, buildExecutionHints, isAgentContinuePrompt, isAgentRetryPrompt, isBuiltContinuePrompt, isBuiltRetryPrompt, } from '../utils/requestIntent.js';
21
21
  import { resolveAgentRoute } from '../utils/agentRoute.js';
22
22
  import { runTemplateInstantPath } from '../utils/templateInstantPath.js';
23
23
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
24
24
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
25
25
  if (!rawValue) {
26
- return 300000;
26
+ return 0;
27
27
  }
28
28
  const parsed = Number.parseInt(rawValue, 10);
29
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
29
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
30
30
  })();
31
31
  const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
32
32
  const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
33
33
  if (!rawValue) {
34
- return 90000;
34
+ return 0;
35
35
  }
36
36
  const parsed = Number.parseInt(rawValue, 10);
37
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 90000;
37
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
38
38
  })();
39
39
  const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
40
- const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS;
41
- if (!rawValue) {
42
- return 180000;
43
- }
40
+ const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS || '300000';
44
41
  const parsed = Number.parseInt(rawValue, 10);
45
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 180000;
42
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
46
43
  })();
47
44
  export class ChatCommand {
48
45
  config;
@@ -1408,6 +1405,9 @@ export class ChatCommand {
1408
1405
  this.directToolContinuationCount = 0;
1409
1406
  this.tools = new AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
1410
1407
  this.initializeSession(options.resume === true || options.retry === true || options.continue === true);
1408
+ if (options.resume && this.lastAgentRunOutcome && !this.jsonOutput && !options.prompt) {
1409
+ this.printResumeContinuationHint();
1410
+ }
1411
1411
  // ── Commando Bridge: connect if --bridge was specified ──────────
1412
1412
  if (options.bridge) {
1413
1413
  const bridgeClient = new BridgeClient({
@@ -1680,7 +1680,11 @@ export class ChatCommand {
1680
1680
  this.operatorMode = this.currentSession.operatorMode || this.operatorMode;
1681
1681
  this.currentModel = this.currentSession.model || this.currentModel;
1682
1682
  if (this.currentSession.lastAgentRunOutcome) {
1683
- this.lastAgentRunOutcome = this.currentSession.lastAgentRunOutcome;
1683
+ const saved = this.currentSession.lastAgentRunOutcome;
1684
+ this.lastAgentRunOutcome = {
1685
+ ...saved,
1686
+ originalPrompt: saved.originalPrompt ?? null,
1687
+ };
1684
1688
  }
1685
1689
  return;
1686
1690
  }
@@ -2168,7 +2172,8 @@ export class ChatCommand {
2168
2172
  if (!this.tools) {
2169
2173
  throw new Error('Agent tools are not initialized.');
2170
2174
  }
2171
- this.lastAgentRoute = await resolveAgentRoute(this.api, prompt);
2175
+ const resolvedPrompt = this.resolveAgentTurnPrompt(prompt);
2176
+ this.lastAgentRoute = await resolveAgentRoute(this.api, resolvedPrompt);
2172
2177
  if (!this.jsonOutput) {
2173
2178
  const r = this.lastAgentRoute;
2174
2179
  const routerNote = r.routerLatencyMs
@@ -2176,13 +2181,13 @@ export class ChatCommand {
2176
2181
  : '';
2177
2182
  this.logger.debug(`Agent route: ${r.path} / ${r.taskKind} [${r.source}]${routerNote} — ${r.reason}`);
2178
2183
  }
2179
- const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
2180
- const handledByTemplateInstant = await this.tryTemplateInstantPath(prompt);
2184
+ const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(resolvedPrompt);
2185
+ const handledByTemplateInstant = await this.tryTemplateInstantPath(resolvedPrompt);
2181
2186
  if (handledByTemplateInstant) {
2182
2187
  this.saveSession();
2183
2188
  return;
2184
2189
  }
2185
- const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(prompt);
2190
+ const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(resolvedPrompt);
2186
2191
  if (handledByDirectFileFlow) {
2187
2192
  this.saveSession();
2188
2193
  return;
@@ -2190,12 +2195,12 @@ export class ChatCommand {
2190
2195
  // Prime the message context with the target file when the direct-file flow was
2191
2196
  // bypassed (e.g. HTML files routed to V3) so the local agent loop has file
2192
2197
  // awareness and the ⚙ Executing: read_file banner is always emitted.
2193
- await this.primeBypassedTargetFileContext(prompt);
2194
- if (this.shouldPreferLocalAgentLoop(prompt)) {
2195
- await this.runLocalAgentLoop(prompt);
2198
+ await this.primeBypassedTargetFileContext(resolvedPrompt);
2199
+ if (this.shouldPreferLocalAgentLoop(resolvedPrompt)) {
2200
+ await this.runLocalAgentLoop(resolvedPrompt);
2196
2201
  return;
2197
2202
  }
2198
- const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt);
2203
+ const handledByV3Workflow = await this.tryV3AgentWorkflow(resolvedPrompt);
2199
2204
  if (handledByV3Workflow) {
2200
2205
  this.saveSession();
2201
2206
  return;
@@ -2207,7 +2212,95 @@ export class ChatCommand {
2207
2212
  this.saveSession();
2208
2213
  return;
2209
2214
  }
2210
- await this.runLocalAgentLoop(prompt);
2215
+ await this.runLocalAgentLoop(resolvedPrompt);
2216
+ }
2217
+ resolveAgentTurnPrompt(prompt) {
2218
+ if (isBuiltContinuePrompt(prompt) || isBuiltRetryPrompt(prompt)) {
2219
+ return prompt;
2220
+ }
2221
+ if (isAgentContinuePrompt(prompt)) {
2222
+ const followUp = this.buildContinuePrompt();
2223
+ if (followUp) {
2224
+ if (!this.jsonOutput) {
2225
+ console.log(chalk.cyan('↪ Treating your message as /continue — resuming the previous agent run.'));
2226
+ }
2227
+ return followUp;
2228
+ }
2229
+ }
2230
+ if (isAgentRetryPrompt(prompt)) {
2231
+ const followUp = this.buildRetryPrompt();
2232
+ if (followUp) {
2233
+ if (!this.jsonOutput) {
2234
+ console.log(chalk.cyan('↪ Treating your message as /retry — re-running failed tasks from the previous run.'));
2235
+ }
2236
+ return followUp;
2237
+ }
2238
+ }
2239
+ return prompt;
2240
+ }
2241
+ shouldPreserveBuildOutcomeForContinue(runPrompt, prior) {
2242
+ if (this.taskRequiresWorkspaceChanges(runPrompt)) {
2243
+ return false;
2244
+ }
2245
+ if (isBuiltContinuePrompt(runPrompt) || isBuiltRetryPrompt(runPrompt)) {
2246
+ return false;
2247
+ }
2248
+ const chainPrompt = prior.originalPrompt || prior.prompt;
2249
+ if (!this.taskRequiresWorkspaceChanges(chainPrompt)) {
2250
+ return false;
2251
+ }
2252
+ return isAgentContinuePrompt(runPrompt);
2253
+ }
2254
+ resolveOriginalPrompt(runPrompt) {
2255
+ const prior = this.lastAgentRunOutcome;
2256
+ if (prior?.originalPrompt) {
2257
+ return prior.originalPrompt;
2258
+ }
2259
+ if (this.taskRequiresWorkspaceChanges(runPrompt) && !isBuiltContinuePrompt(runPrompt) && !isBuiltRetryPrompt(runPrompt)) {
2260
+ return runPrompt;
2261
+ }
2262
+ if (prior && this.taskRequiresWorkspaceChanges(prior.originalPrompt || prior.prompt)) {
2263
+ return prior.originalPrompt || prior.prompt;
2264
+ }
2265
+ return null;
2266
+ }
2267
+ commitAgentRunOutcome(outcome, runPrompt) {
2268
+ const prior = this.lastAgentRunOutcome;
2269
+ if (prior && this.shouldPreserveBuildOutcomeForContinue(runPrompt, prior)) {
2270
+ if (!this.jsonOutput) {
2271
+ console.log(chalk.yellow('↪ Keeping the previous build run state — use /continue to resume implementation.'));
2272
+ }
2273
+ return;
2274
+ }
2275
+ outcome.originalPrompt = this.resolveOriginalPrompt(runPrompt);
2276
+ this.lastAgentRunOutcome = outcome;
2277
+ }
2278
+ printResumeContinuationHint() {
2279
+ const outcome = this.lastAgentRunOutcome;
2280
+ if (!outcome) {
2281
+ return;
2282
+ }
2283
+ const sourcePrompt = outcome.originalPrompt || outcome.prompt;
2284
+ const isBuild = this.taskRequiresWorkspaceChanges(sourcePrompt);
2285
+ const openWork = outcome.failedTaskIds.length > 0
2286
+ || outcome.unfinishedTaskIds.length > 0
2287
+ || (outcome.tasksTotal > 0 && outcome.tasksSucceeded < outcome.tasksTotal);
2288
+ if (!isBuild && !openWork) {
2289
+ return;
2290
+ }
2291
+ console.log('');
2292
+ console.log(chalk.cyan('Prior agent run restored for this project.'));
2293
+ if (openWork) {
2294
+ const taskIds = [...new Set([...outcome.failedTaskIds, ...outcome.unfinishedTaskIds])].slice(0, 8);
2295
+ if (taskIds.length > 0) {
2296
+ console.log(chalk.gray(` Unfinished tasks: ${taskIds.join(', ')}`));
2297
+ }
2298
+ else {
2299
+ console.log(chalk.gray(` Progress: ${outcome.tasksSucceeded}/${outcome.tasksTotal} tasks completed`));
2300
+ }
2301
+ }
2302
+ console.log(chalk.gray(' Type ') + chalk.cyan('/continue') + chalk.gray(' to resume implementation, or ') + chalk.cyan('/retry') + chalk.gray(' for failed tasks only.'));
2303
+ console.log('');
2211
2304
  }
2212
2305
  buildLocalLoopLiveOutcome(prompt) {
2213
2306
  const liveOutcome = createLiveOutcome();
@@ -2223,8 +2316,9 @@ export class ChatCommand {
2223
2316
  return liveOutcome;
2224
2317
  }
2225
2318
  persistLocalLoopOutcome(prompt, evaluation, liveOutcome) {
2226
- this.lastAgentRunOutcome = {
2319
+ this.commitAgentRunOutcome({
2227
2320
  prompt,
2321
+ originalPrompt: null,
2228
2322
  taskId: null,
2229
2323
  contextId: null,
2230
2324
  tasksSucceeded: liveOutcome.tasksSucceeded,
@@ -2245,7 +2339,7 @@ export class ChatCommand {
2245
2339
  workspacePath: this.currentProjectPath || null,
2246
2340
  workspaceSyncIssue: null,
2247
2341
  finishedAt: Date.now(),
2248
- };
2342
+ }, prompt);
2249
2343
  }
2250
2344
  async runLocalAgentLoop(prompt) {
2251
2345
  if (!this.tools) {
@@ -2583,8 +2677,9 @@ export class ChatCommand {
2583
2677
  liveOutcome.tasksSucceeded = 1;
2584
2678
  liveOutcome.answerContent = summary;
2585
2679
  const evaluation = evaluateExecutorSuccess(liveOutcome);
2586
- this.lastAgentRunOutcome = {
2680
+ this.commitAgentRunOutcome({
2587
2681
  prompt,
2682
+ originalPrompt: null,
2588
2683
  taskId: null,
2589
2684
  contextId: null,
2590
2685
  tasksSucceeded: 1,
@@ -2605,7 +2700,7 @@ export class ChatCommand {
2605
2700
  workspacePath: workspacePath || null,
2606
2701
  workspaceSyncIssue: null,
2607
2702
  finishedAt: Date.now(),
2608
- };
2703
+ }, prompt);
2609
2704
  if (!this.jsonOutput) {
2610
2705
  console.log(chalk.green(`✓ ${summary}`));
2611
2706
  console.log(chalk.gray(' Run: vigthoria preview'));
@@ -3201,8 +3296,9 @@ export class ChatCommand {
3201
3296
  if (!executorSucceeded) {
3202
3297
  process.exitCode = 1;
3203
3298
  }
3204
- this.lastAgentRunOutcome = {
3299
+ this.commitAgentRunOutcome({
3205
3300
  prompt,
3301
+ originalPrompt: null,
3206
3302
  taskId: response.taskId || null,
3207
3303
  contextId: response.contextId || null,
3208
3304
  tasksSucceeded: liveOutcome.tasksSucceeded,
@@ -3225,9 +3321,11 @@ export class ChatCommand {
3225
3321
  ? 'Local workspace has files but the agent did not confirm tracked changes.'
3226
3322
  : null,
3227
3323
  finishedAt: Date.now(),
3228
- };
3324
+ }, prompt);
3229
3325
  if (!this.jsonOutput && !this.directPromptMode) {
3230
- this.printAgentRunSummary(this.lastAgentRunOutcome, runEvaluation, changedFileCount);
3326
+ if (this.lastAgentRunOutcome) {
3327
+ this.printAgentRunSummary(this.lastAgentRunOutcome, runEvaluation, changedFileCount);
3328
+ }
3231
3329
  }
3232
3330
  if (this.jsonOutput) {
3233
3331
  if (!executorSucceeded) {
@@ -3299,8 +3397,9 @@ export class ChatCommand {
3299
3397
  taskDisplay.finalize();
3300
3398
  }
3301
3399
  catch (_) { /* render is best-effort */ }
3302
- this.lastAgentRunOutcome = {
3400
+ this.commitAgentRunOutcome({
3303
3401
  prompt,
3402
+ originalPrompt: null,
3304
3403
  taskId: null,
3305
3404
  contextId: null,
3306
3405
  tasksSucceeded: liveOutcome.tasksSucceeded,
@@ -3321,7 +3420,7 @@ export class ChatCommand {
3321
3420
  workspacePath: workspacePath || null,
3322
3421
  workspaceSyncIssue: null,
3323
3422
  finishedAt: Date.now(),
3324
- };
3423
+ }, prompt);
3325
3424
  if (!this.jsonOutput) {
3326
3425
  process.exitCode = 1;
3327
3426
  }
@@ -3349,7 +3448,9 @@ export class ChatCommand {
3349
3448
  requiresWorkspaceChanges: this.taskRequiresWorkspaceChanges(prompt),
3350
3449
  workspaceHasOutput: this.api.hasAgentWorkspaceOutput(workspaceContext),
3351
3450
  });
3352
- this.printAgentRunSummary(this.lastAgentRunOutcome, failedEval, 0);
3451
+ if (this.lastAgentRunOutcome) {
3452
+ this.printAgentRunSummary(this.lastAgentRunOutcome, failedEval, 0);
3453
+ }
3353
3454
  }
3354
3455
  return true;
3355
3456
  }
@@ -3800,6 +3901,7 @@ export class ChatCommand {
3800
3901
  const o = this.lastAgentRunOutcome;
3801
3902
  if (!o || !o.prompt)
3802
3903
  return null;
3904
+ const sourcePrompt = o.originalPrompt || o.prompt;
3803
3905
  const runtime = this.getRuntimeEnvironmentContext();
3804
3906
  const remaining = [...new Set([...o.failedTaskIds, ...o.unfinishedTaskIds])];
3805
3907
  const taskList = remaining.length > 0 ? remaining.join(', ') : '';
@@ -3814,9 +3916,9 @@ export class ChatCommand {
3814
3916
  ? '\nUse local workspace tools and local filesystem paths first; do not assume server-side workspace access.'
3815
3917
  : '';
3816
3918
  if (taskList) {
3817
- return `Resume the previous agent run. Re-execute only these tasks and make them pass: ${taskList}.${blockerLine}${missingLine}${envLine}${localPreferenceLine}\nOriginal request was: ${o.prompt}`;
3919
+ return `Resume the previous agent run. Re-execute only these tasks and make them pass: ${taskList}.${blockerLine}${missingLine}${envLine}${localPreferenceLine}\nOriginal request was: ${sourcePrompt}`;
3818
3920
  }
3819
- return `Retry the previous request and make sure it finishes successfully.${blockerLine}${missingLine}${envLine}${localPreferenceLine}\nOriginal request was: ${o.prompt}`;
3921
+ return `Retry the previous request and make sure it finishes successfully.${blockerLine}${missingLine}${envLine}${localPreferenceLine}\nOriginal request was: ${sourcePrompt}`;
3820
3922
  }
3821
3923
  computeRetryPromptSignature() {
3822
3924
  const o = this.lastAgentRunOutcome;
@@ -3834,9 +3936,10 @@ export class ChatCommand {
3834
3936
  const o = this.lastAgentRunOutcome;
3835
3937
  if (!o || !o.prompt)
3836
3938
  return null;
3939
+ const sourcePrompt = o.originalPrompt || o.prompt;
3837
3940
  const remaining = [...new Set([...o.failedTaskIds, ...o.unfinishedTaskIds])];
3838
3941
  const taskList = remaining.length > 0 ? `\nRemaining tasks to finish: ${remaining.join(', ')}.` : '';
3839
- const mustChangeFiles = this.taskRequiresWorkspaceChanges(o.prompt)
3942
+ const mustChangeFiles = this.taskRequiresWorkspaceChanges(sourcePrompt)
3840
3943
  ? '\nThis was a build/edit request. Do not stop after analysis or tool JSON; make real workspace file changes and verify them before finishing.'
3841
3944
  : '';
3842
3945
  const blockerLine = o.qualityBlockers.length > 0
@@ -3845,7 +3948,7 @@ export class ChatCommand {
3845
3948
  const missingLine = o.qualityMissing.length > 0
3846
3949
  ? `\nMissing pieces: ${o.qualityMissing.slice(0, 6).join(', ')}.`
3847
3950
  : '';
3848
- return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${mustChangeFiles}${blockerLine}${missingLine}\nOriginal request was: ${o.prompt}`;
3951
+ return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${mustChangeFiles}${blockerLine}${missingLine}\nOriginal request was: ${sourcePrompt}`;
3849
3952
  }
3850
3953
  /**
3851
3954
  * Re-print the last agent run summary, or guide the user when there isn't one.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Unified agent routing: regex (instant) → optional Balanced 4B → V3 execution hints.
3
3
  */
4
- import { inferAgentTaskType, isTrivialHtmlPageRequest, isContinueOrRetryPrompt, hasGameIntent, hasWebPageIntent, taskRequiresWorkspaceChanges, stripExecutionShaping, } from './requestIntent.js';
4
+ import { inferAgentTaskType, isTrivialHtmlPageRequest, isAgentContinuePrompt, isAgentRetryPrompt, hasGameIntent, hasWebPageIntent, taskRequiresWorkspaceChanges, stripExecutionShaping, } from './requestIntent.js';
5
5
  import { isFastInferenceRouterAvailable, routeWithBalanced4b, shouldUseBalancedRouter, } from './fastAgentRouter.js';
6
6
  function normalizeTaskKind(value, fallback) {
7
7
  const kind = String(value || fallback).trim().toLowerCase();
@@ -74,13 +74,23 @@ function mergeLlmRoute(regexTask, prompt, parsed) {
74
74
  }
75
75
  export async function resolveAgentRoute(api, prompt) {
76
76
  const stripped = stripExecutionShaping(prompt);
77
- if (isContinueOrRetryPrompt(stripped)) {
77
+ if (isAgentContinuePrompt(stripped)) {
78
78
  const regexTask = inferAgentTaskType(stripped);
79
79
  return {
80
80
  path: 'v3-agent',
81
81
  taskKind: regexTask === 'analysis' ? 'implementation' : regexTask,
82
- confidence: 0.9,
83
- reason: 'continue/retry follow-up (regex)',
82
+ confidence: 0.92,
83
+ reason: 'continue follow-up (regex)',
84
+ source: 'regex',
85
+ };
86
+ }
87
+ if (isAgentRetryPrompt(stripped)) {
88
+ const regexTask = inferAgentTaskType(stripped);
89
+ return {
90
+ path: 'v3-agent',
91
+ taskKind: regexTask === 'analysis' ? 'implementation' : regexTask,
92
+ confidence: 0.92,
93
+ reason: 'retry follow-up (regex)',
84
94
  source: 'regex',
85
95
  };
86
96
  }
package/dist/utils/api.js CHANGED
@@ -259,15 +259,18 @@ export function propagateError(err) {
259
259
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
260
260
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
261
261
  if (!rawValue) {
262
- return 300000;
262
+ return 0;
263
263
  }
264
264
  const parsed = Number.parseInt(rawValue, 10);
265
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
265
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
266
266
  })();
267
267
  const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
268
- const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS || '90000';
268
+ const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
269
+ if (!rawValue) {
270
+ return 0;
271
+ }
269
272
  const parsed = Number.parseInt(rawValue, 10);
270
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 90000;
273
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
271
274
  })();
272
275
  const DEFAULT_OPERATOR_TIMEOUT_MS = (() => {
273
276
  const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS;
@@ -2643,48 +2646,18 @@ menu {
2643
2646
  }
2644
2647
  return;
2645
2648
  }
2646
- if (event.type === 'workspace_snapshot' && event.files && typeof event.files === 'object') {
2647
- for (const [rawPath, content] of Object.entries(event.files)) {
2648
- if (typeof content !== 'string') {
2649
- continue;
2650
- }
2651
- const filePath = this.normalizeAgentWorkspaceRelativePath(rawPath, serverRoot || undefined);
2652
- if (filePath) {
2653
- streamedFiles[filePath] = content;
2654
- }
2655
- }
2656
- return;
2657
- }
2658
- if (event.type !== 'tool_call') {
2659
- if (event.type === 'tool_result' && event.success) {
2660
- const name = String(event.name || event.tool || '').trim();
2661
- const args = event.arguments || {};
2662
- const targetPath = typeof event.target === 'string'
2663
- ? event.target
2664
- : (typeof args.path === 'string' ? args.path : '');
2665
- if ((name === 'write_file' || name === 'edit_file') && targetPath) {
2666
- const filePath = this.normalizeAgentWorkspaceRelativePath(targetPath, serverRoot || undefined);
2667
- if (filePath && typeof args.content === 'string') {
2668
- streamedFiles[filePath] = args.content;
2669
- }
2670
- }
2671
- }
2672
- return;
2673
- }
2674
- const args = event.arguments || {};
2675
- if ((event.name === 'write_file' || event.name === 'edit_file') && typeof args.path === 'string') {
2676
- const filePath = this.normalizeAgentWorkspaceRelativePath(args.path, serverRoot || undefined);
2677
- if (!filePath) {
2678
- return;
2679
- }
2680
- if (event.name === 'write_file' && typeof args.content === 'string') {
2681
- streamedFiles[filePath] = args.content;
2682
- return;
2683
- }
2684
- if (event.name === 'edit_file' && typeof args.old_string === 'string' && typeof args.new_string === 'string') {
2685
- const existing = streamedFiles[filePath];
2686
- if (typeof existing === 'string' && existing.includes(args.old_string)) {
2687
- streamedFiles[filePath] = existing.replace(args.old_string, args.new_string);
2649
+ // Only count confirmed mutations not workspace snapshots (hydrated baseline)
2650
+ // or tool_call previews that may still fail client-side / read-only guards.
2651
+ if (event.type === 'tool_result' && event.success) {
2652
+ const name = String(event.name || event.tool || '').trim();
2653
+ const args = event.arguments || {};
2654
+ const targetPath = typeof event.target === 'string'
2655
+ ? event.target
2656
+ : (typeof args.path === 'string' ? args.path : '');
2657
+ if ((name === 'write_file' || name === 'edit_file') && targetPath) {
2658
+ const filePath = this.normalizeAgentWorkspaceRelativePath(targetPath, serverRoot || undefined);
2659
+ if (filePath && typeof args.content === 'string') {
2660
+ streamedFiles[filePath] = args.content;
2688
2661
  }
2689
2662
  }
2690
2663
  }
@@ -2851,9 +2824,30 @@ menu {
2851
2824
  return { success: true, output: matches.slice(0, 100).join('\n') || '(no matches)' };
2852
2825
  }
2853
2826
  if (name === 'syntax_check') {
2854
- const content = fs.readFileSync(target.absolutePath, 'utf8');
2855
- if (/\.json$/i.test(target.absolutePath))
2856
- JSON.parse(content);
2827
+ if (!fs.existsSync(target.absolutePath)) {
2828
+ return {
2829
+ success: false,
2830
+ output: '',
2831
+ error: `File not found: ${target.relativePath}. Write the file before running syntax_check.`,
2832
+ };
2833
+ }
2834
+ const ext = path.extname(target.absolutePath).toLowerCase();
2835
+ if (ext === '.json') {
2836
+ JSON.parse(fs.readFileSync(target.absolutePath, 'utf8'));
2837
+ return { success: true, output: `Syntax check passed: ${target.relativePath}` };
2838
+ }
2839
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
2840
+ const { execSync } = await import('child_process');
2841
+ try {
2842
+ execSync(`node --check "${target.absolutePath}"`, { stdio: 'pipe' });
2843
+ return { success: true, output: `Syntax check passed: ${target.relativePath}` };
2844
+ }
2845
+ catch (error) {
2846
+ const stderr = error?.stderr?.toString?.() || error?.message || String(error);
2847
+ return { success: false, output: '', error: stderr.trim() || `Syntax check failed: ${target.relativePath}` };
2848
+ }
2849
+ }
2850
+ fs.readFileSync(target.absolutePath, 'utf8');
2857
2851
  return { success: true, output: `Syntax check passed: ${target.relativePath}` };
2858
2852
  }
2859
2853
  if (name === 'run_command') {
@@ -3563,48 +3557,71 @@ document.addEventListener('DOMContentLoaded', () => {
3563
3557
  this.activeV3StreamContext = context;
3564
3558
  context.__v3StreamedFiles = streamedFiles;
3565
3559
  const idleTimeoutMs = (() => {
3566
- const base = context.agentIdleTimeoutMs || DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS;
3567
- if (isPlannerBuildTask(String(context.agentTaskType || ''), String(context.rawPrompt || ''))) {
3568
- const raw = process.env.VIGTHORIA_AGENT_PLANNER_IDLE_TIMEOUT_MS || '300000';
3569
- const extended = Number.parseInt(raw, 10);
3570
- return Math.max(base, Number.isFinite(extended) && extended > 0 ? extended : 300000);
3560
+ const base = context.agentIdleTimeoutMs ?? DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS;
3561
+ if (base <= 0) {
3562
+ return 0;
3571
3563
  }
3572
- return base;
3564
+ if (!isPlannerBuildTask(String(context.agentTaskType || ''), String(context.rawPrompt || ''))) {
3565
+ return base;
3566
+ }
3567
+ const raw = process.env.VIGTHORIA_AGENT_PLANNER_IDLE_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_IDLE_TIMEOUT_MS;
3568
+ if (!raw) {
3569
+ return base;
3570
+ }
3571
+ const extended = Number.parseInt(raw, 10);
3572
+ return Number.isFinite(extended) && extended > 0 ? Math.max(base, extended) : base;
3573
3573
  })();
3574
3574
  try {
3575
3575
  while (true) {
3576
3576
  let chunk;
3577
3577
  try {
3578
- const readPromise = reader.read();
3579
- while (true) {
3580
- const timeoutSentinel = Symbol('v3-agent-idle-timeout');
3581
- const result = idleTimeoutMs > 0
3582
- ? await Promise.race([
3583
- readPromise,
3584
- new Promise((resolve) => {
3585
- setTimeout(() => resolve(timeoutSentinel), idleTimeoutMs);
3586
- }),
3587
- ])
3588
- : await readPromise;
3589
- if (result !== timeoutSentinel) {
3590
- chunk = result;
3591
- break;
3592
- }
3593
- if (this.pendingV3ClientToolTasks.size > 0) {
3594
- continue;
3595
- }
3596
- if (this.hasAgentWorkspaceOutput(context)) {
3597
- const stalledError = new Error('V3 agent stream stalled after writing workspace output');
3598
- stalledError.name = 'AbortError';
3599
- stalledError.partialData = {
3600
- task_id: events.find((event) => event && event.task_id)?.task_id || null,
3601
- context_id: contextId,
3602
- result: final,
3603
- events,
3604
- files: streamedFiles,
3578
+ if (idleTimeoutMs <= 0) {
3579
+ chunk = await reader.read();
3580
+ }
3581
+ else {
3582
+ chunk = await new Promise((resolve, reject) => {
3583
+ let idleTimer = null;
3584
+ const clearIdleTimer = () => {
3585
+ if (idleTimer) {
3586
+ clearTimeout(idleTimer);
3587
+ idleTimer = null;
3588
+ }
3605
3589
  };
3606
- throw stalledError;
3607
- }
3590
+ const scheduleIdleTimer = () => {
3591
+ clearIdleTimer();
3592
+ idleTimer = setTimeout(() => {
3593
+ if (this.pendingV3ClientToolTasks.size > 0) {
3594
+ scheduleIdleTimer();
3595
+ return;
3596
+ }
3597
+ if (this.hasAgentWorkspaceOutput(context)) {
3598
+ const stalledError = new Error('V3 agent stream stalled after writing workspace output');
3599
+ stalledError.name = 'AbortError';
3600
+ stalledError.partialData = {
3601
+ task_id: events.find((event) => event && event.task_id)?.task_id || null,
3602
+ context_id: contextId,
3603
+ result: final,
3604
+ events,
3605
+ files: streamedFiles,
3606
+ };
3607
+ clearIdleTimer();
3608
+ reject(stalledError);
3609
+ return;
3610
+ }
3611
+ scheduleIdleTimer();
3612
+ }, idleTimeoutMs);
3613
+ };
3614
+ scheduleIdleTimer();
3615
+ reader.read()
3616
+ .then((result) => {
3617
+ clearIdleTimer();
3618
+ resolve(result);
3619
+ })
3620
+ .catch((error) => {
3621
+ clearIdleTimer();
3622
+ reject(error);
3623
+ });
3624
+ });
3608
3625
  }
3609
3626
  }
3610
3627
  catch (error) {
@@ -3770,11 +3787,7 @@ document.addEventListener('DOMContentLoaded', () => {
3770
3787
  const resolvedModel = this.resolvePermittedModelId(requestedModel);
3771
3788
  const preferLocalV3 = /(premium|polished|landing|site|page|dashboard|saas|frontend|ui|responsive|animated|create the required project files and write them to the workspace)/i.test(message)
3772
3789
  && context.localMachineCapable !== false;
3773
- const rescueEligibleSaaS = preferLocalV3
3774
- && /(saas|dashboard|analytics|billing|team management|activity feed|login screen)/i.test(message);
3775
- const timeoutMs = rescueEligibleSaaS
3776
- ? Math.min(resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || '')), 210000)
3777
- : resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || ''));
3790
+ const timeoutMs = resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || ''));
3778
3791
  const maxAttempts = preferLocalV3 ? 2 : 1;
3779
3792
  let lastErrors = [];
3780
3793
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
@@ -5560,11 +5573,20 @@ document.addEventListener('DOMContentLoaded', () => {
5560
5573
  'vigthoria-code': 'vigthoria-v3-code-35b',
5561
5574
  'vigthoria-agent': 'vigthoria-v3-code-35b',
5562
5575
  // ═══════════════════════════════════════════════════════════════
5563
- // VIGTHORIA CLOUD - Premium cloud models (internal routing)
5576
+ // VIGTHORIA CLOUD - current billing catalog aliases
5564
5577
  // ═══════════════════════════════════════════════════════════════
5565
- 'cloud': 'vigthoria-cloud-pro',
5566
- 'cloud-reason': 'vigthoria-cloud-k2',
5567
- 'ultra': 'vigthoria-cloud-ultra',
5578
+ 'cloud': 'vigthoria-cloud-balanced',
5579
+ 'cloud-code': 'vigthoria-cloud-code',
5580
+ 'cloud-balanced': 'vigthoria-cloud-balanced',
5581
+ 'cloud-fast': 'vigthoria-cloud-fast',
5582
+ 'cloud-power': 'vigthoria-cloud-power',
5583
+ 'cloud-maximum': 'vigthoria-cloud-maximum',
5584
+ 'cloud-reason': 'vigthoria-cloud-power',
5585
+ 'ultra': 'vigthoria-cloud-maximum',
5586
+ // Legacy aliases kept for backward compatibility
5587
+ 'cloud-pro': 'vigthoria-cloud-power',
5588
+ 'cloud-k2': 'vigthoria-cloud-power',
5589
+ 'cloud-ultra': 'vigthoria-cloud-maximum',
5568
5590
  };
5569
5591
  // If already a full model ID, return as-is
5570
5592
  if (shortName.includes('vigthoria') || shortName.includes('/') || shortName.includes(':')) {
@@ -7,6 +7,12 @@ export declare function stripExecutionShaping(prompt: string): string;
7
7
  export declare function hasBuildVerb(prompt: string): boolean;
8
8
  export declare function hasArtifactNoun(prompt: string): boolean;
9
9
  export declare function hasGameIntent(prompt: string): boolean;
10
+ /** True when the CLI already expanded a /continue-style follow-up prompt. */
11
+ export declare function isBuiltContinuePrompt(prompt: string): boolean;
12
+ /** True when the CLI already expanded a /retry-style follow-up prompt. */
13
+ export declare function isBuiltRetryPrompt(prompt: string): boolean;
14
+ export declare function isAgentContinuePrompt(prompt: string): boolean;
15
+ export declare function isAgentRetryPrompt(prompt: string): boolean;
10
16
  export declare function isContinueOrRetryPrompt(prompt: string): boolean;
11
17
  export declare function hasWebPageIntent(prompt: string): boolean;
12
18
  export declare function isTrivialHtmlPageRequest(prompt: string): boolean;
@@ -41,9 +41,57 @@ export function hasGameIntent(prompt) {
41
41
  }
42
42
  return false;
43
43
  }
44
+ const CONTINUE_VERB = /\b(?:continue|contiune|continu|proceed|resume|keep\s+going|go\s+on|carry\s+on|pick\s+up\s+where)\b/i;
45
+ const CONTINUE_OBJECT = /\b(?:task|tasks|work|build|building|implementation|project|run|plan|planning|remaining|unfinished|missing|components?|next\s+steps?|previous|last|where\s+we\s+left|hyperloop|report)\b/i;
46
+ /** True when the CLI already expanded a /continue-style follow-up prompt. */
47
+ export function isBuiltContinuePrompt(prompt) {
48
+ return /^Continue the previous agent run/i.test(stripExecutionShaping(prompt));
49
+ }
50
+ /** True when the CLI already expanded a /retry-style follow-up prompt. */
51
+ export function isBuiltRetryPrompt(prompt) {
52
+ const text = stripExecutionShaping(prompt);
53
+ return /^(?:Resume the previous agent run|Retry the previous request)/i.test(text);
54
+ }
55
+ export function isAgentContinuePrompt(prompt) {
56
+ const text = stripExecutionShaping(prompt).trim();
57
+ if (!text) {
58
+ return false;
59
+ }
60
+ if (isBuiltContinuePrompt(text) || isBuiltRetryPrompt(text)) {
61
+ return false;
62
+ }
63
+ if (/^\/(?:continue|contiune|contiunue)$/i.test(text)) {
64
+ return true;
65
+ }
66
+ if (/\bcontinue\s+(?:the\s+)?(?:previous|last|current)\s+(?:agent\s+)?run\b/i.test(text)) {
67
+ return true;
68
+ }
69
+ if (/\bresume\s+(?:the\s+)?(?:failed|unfinished)\b/i.test(text)) {
70
+ return true;
71
+ }
72
+ if (/\bplanner\s+produced\s+\d+\s+execution\s+tasks\b/i.test(text)) {
73
+ return true;
74
+ }
75
+ if (CONTINUE_VERB.test(text) && CONTINUE_OBJECT.test(text)) {
76
+ return true;
77
+ }
78
+ if (/^(?:please\s+)?(?:continue|contiune|proceed)(?:[!?.]|$)/i.test(text)) {
79
+ return true;
80
+ }
81
+ return false;
82
+ }
83
+ export function isAgentRetryPrompt(prompt) {
84
+ const text = stripExecutionShaping(prompt).trim();
85
+ if (!text || isBuiltContinuePrompt(text) || isBuiltRetryPrompt(text)) {
86
+ return false;
87
+ }
88
+ if (/^\/retry$/i.test(text)) {
89
+ return true;
90
+ }
91
+ return /\b(?:retry|re-?try)\s+(?:the\s+)?(?:failed|last|previous|unfinished)\b/i.test(text);
92
+ }
44
93
  export function isContinueOrRetryPrompt(prompt) {
45
- const text = stripExecutionShaping(prompt).toLowerCase();
46
- return /\b(continue\s+(?:the\s+)?(?:previous|last|current)\s+(?:agent\s+)?run|\/retry|\/continue|resume\s+(?:the\s+)?(?:failed|unfinished)|planner\s+produced\s+\d+\s+execution\s+tasks)\b/i.test(text);
94
+ return isAgentContinuePrompt(prompt) || isAgentRetryPrompt(prompt);
47
95
  }
48
96
  export function hasWebPageIntent(prompt) {
49
97
  const text = stripExecutionShaping(prompt);
@@ -150,16 +198,24 @@ export function resolvePlannerAgentTimeoutMs(baseTimeoutMs, agentTaskType, promp
150
198
  if (!isPlannerBuildTask(agentTaskType, prompt)) {
151
199
  return baseTimeoutMs;
152
200
  }
153
- const raw = process.env.VIGTHORIA_AGENT_PLANNER_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_TIMEOUT_MS || '900000';
201
+ const raw = process.env.VIGTHORIA_AGENT_PLANNER_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_TIMEOUT_MS;
202
+ if (!raw) {
203
+ return baseTimeoutMs;
204
+ }
154
205
  const extended = Number.parseInt(raw, 10);
155
- const floor = Number.isFinite(extended) && extended > 0 ? extended : 900000;
156
- return Math.max(baseTimeoutMs, floor);
206
+ if (!Number.isFinite(extended) || extended <= 0) {
207
+ return baseTimeoutMs;
208
+ }
209
+ return Math.max(baseTimeoutMs, extended);
157
210
  }
158
211
  /** V3 workflow mode: suppress write path when the user did not ask for file changes. */
159
212
  export function resolveWorkflowType(agentTaskType, prompt) {
160
213
  if (hasAnalyzeAndBuildIntent(prompt)) {
161
214
  return 'full';
162
215
  }
216
+ if (isBuiltContinuePrompt(prompt) || isBuiltRetryPrompt(prompt)) {
217
+ return taskRequiresWorkspaceChanges(prompt) ? 'full' : 'analysis_only';
218
+ }
163
219
  if (!taskRequiresWorkspaceChanges(prompt)) {
164
220
  return 'analysis_only';
165
221
  }
@@ -36,7 +36,7 @@ export function normalizeV3WorkspaceRelativePath(rawPath, rootPath) {
36
36
  return null;
37
37
  };
38
38
  const decodeBoundaryUri = (value) => {
39
- const match = value.match(/^vigthoria:\/\/(?:workspace|server-internal)\/?(.*)$/i);
39
+ const match = value.match(/^vigthoria:\/\/(?:workspace|server-internal|local-workspace)\/?(.*)$/i);
40
40
  if (match) {
41
41
  return safeRelative(match[1] || '');
42
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.25",
3
+ "version": "1.11.28",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",