vigthoria-cli 1.11.0 → 1.11.2

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.
@@ -13,7 +13,7 @@ import { TaskDisplay } from '../utils/task-display.js';
13
13
  import { ProjectMemoryService } from '../utils/project-memory.js';
14
14
  import { buildPersonaOverlay, normalizePersonaMode } from '../utils/persona.js';
15
15
  import { runAgentSessionMenu, shouldShowAgentSessionMenu } from './agent-session-menu.js';
16
- import { evaluateExecutorSuccess, handleTaskEvent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
16
+ import { createLiveOutcome, evaluateExecutorSuccess, handleTaskEvent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
17
17
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
18
18
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
19
19
  if (!rawValue) {
@@ -2368,20 +2368,11 @@ export class ChatCommand {
2368
2368
  }).start();
2369
2369
  // Live run telemetry, used both for the final summary block and for /retry, /continue.
2370
2370
  const liveOutcome = {
2371
- tasksSucceeded: 0,
2372
- tasksTotal: 0,
2373
- failedTaskIds: new Set(),
2371
+ ...createLiveOutcome(),
2374
2372
  unfinishedTaskIds: new Set(),
2375
2373
  qualityScore: null,
2376
2374
  qualityMissing: [],
2377
2375
  qualityBlockers: [],
2378
- plannerError: null,
2379
- executorError: null,
2380
- executorFailed: false,
2381
- analysisToolsUsed: 0,
2382
- changedFileCount: 0,
2383
- requiresWorkspaceChanges: false,
2384
- workspaceHasOutput: false,
2385
2376
  };
2386
2377
  const parsePlannerSummary = (raw) => {
2387
2378
  if (!raw || typeof raw !== 'string')
@@ -2498,7 +2489,7 @@ export class ChatCommand {
2498
2489
  else if (event.type === 'executor_complete') {
2499
2490
  const summary = event.summary || {};
2500
2491
  handleTaskEvent(summary, liveOutcome);
2501
- if (summary.status === 'failed') {
2492
+ if (summary.status === 'failed' || summary.status === 'stalled_error') {
2502
2493
  const err = typeof summary.error === 'string' ? summary.error.trim() : '';
2503
2494
  if (err && !liveOutcome.executorError)
2504
2495
  liveOutcome.executorError = err;
@@ -2510,6 +2501,10 @@ export class ChatCommand {
2510
2501
  noteAnalysisToolUse(toolName, liveOutcome);
2511
2502
  }
2512
2503
  else if (event.type === 'complete') {
2504
+ const tt = Number(event.tasks_total);
2505
+ if (Number.isFinite(tt) && tt > 0) {
2506
+ liveOutcome.tasksTotal = tt;
2507
+ }
2513
2508
  if (Number(event.discovery_tools_used) > 0) {
2514
2509
  liveOutcome.analysisToolsUsed = Math.max(liveOutcome.analysisToolsUsed, Number(event.discovery_tools_used));
2515
2510
  }
@@ -2589,20 +2584,7 @@ export class ChatCommand {
2589
2584
  if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
2590
2585
  console.log(chalk.yellow(`Template Service preview gate did not fully validate this output, but generated workspace files were preserved${previewGate?.error ? `: ${previewGate.error}` : '.'}`));
2591
2586
  }
2592
- if (this.jsonOutput) {
2593
- console.log(JSON.stringify({
2594
- success,
2595
- mode: 'agent',
2596
- model: routingPolicy.selectedModel,
2597
- routingPolicy,
2598
- taskId: response.taskId || null,
2599
- contextId: response.contextId || null,
2600
- partial: response.partial === true,
2601
- content: response.content || 'V3 agent workflow completed.',
2602
- metadata: response.metadata || {},
2603
- }, null, 2));
2604
- }
2605
- else if (this.v3StreamingStarted) {
2587
+ if (this.v3StreamingStarted) {
2606
2588
  // Content was already streamed to stdout in real-time; skip duplicate print.
2607
2589
  if (!this.jsonOutput) {
2608
2590
  console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
@@ -2720,6 +2702,9 @@ export class ChatCommand {
2720
2702
  }
2721
2703
  taskDisplay.finalize();
2722
2704
  // ────────────────────────────────────────────────────────────────
2705
+ if (!executorSucceeded) {
2706
+ process.exitCode = 1;
2707
+ }
2723
2708
  this.lastAgentRunOutcome = {
2724
2709
  prompt,
2725
2710
  taskId: response.taskId || null,
@@ -2747,7 +2732,35 @@ export class ChatCommand {
2747
2732
  if (!this.jsonOutput && !this.directPromptMode) {
2748
2733
  this.printAgentRunSummary(this.lastAgentRunOutcome, runEvaluation, changedFileCount);
2749
2734
  }
2750
- this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
2735
+ if (this.jsonOutput) {
2736
+ if (!executorSucceeded) {
2737
+ process.exitCode = 1;
2738
+ }
2739
+ console.log(JSON.stringify({
2740
+ success: executorSucceeded,
2741
+ mode: 'agent',
2742
+ model: routingPolicy.selectedModel,
2743
+ routingPolicy,
2744
+ taskId: response.taskId || null,
2745
+ contextId: response.contextId || null,
2746
+ partial: response.partial === true || (!executorSucceeded && liveOutcome.tasksSucceeded > 0),
2747
+ content: response.content || runEvaluation.statusHeadline,
2748
+ statusHeadline: runEvaluation.statusHeadline,
2749
+ tasksSucceeded: liveOutcome.tasksSucceeded,
2750
+ tasksTotal: liveOutcome.tasksTotal,
2751
+ metadata: {
2752
+ ...(response.metadata || {}),
2753
+ outcomeTruth: {
2754
+ executorSucceeded,
2755
+ uiTheme: runEvaluation.uiTheme,
2756
+ tasksSucceeded: liveOutcome.tasksSucceeded,
2757
+ tasksTotal: liveOutcome.tasksTotal,
2758
+ },
2759
+ previewGate,
2760
+ },
2761
+ }, null, 2));
2762
+ }
2763
+ this.messages.push({ role: 'assistant', content: response.content || runEvaluation.statusHeadline });
2751
2764
  watcher?.stop();
2752
2765
  return true;
2753
2766
  }
@@ -2776,6 +2789,10 @@ export class ChatCommand {
2776
2789
  : 'Agent mode is unavailable right now. Please retry shortly or run vigthoria login if the issue persists.';
2777
2790
  this.logger.error(errorMessage);
2778
2791
  this.messages.push({ role: 'assistant', content: errorMessage });
2792
+ liveOutcome.streamAborted = true;
2793
+ if (!liveOutcome.executorError) {
2794
+ liveOutcome.executorError = safeDetail || 'Connection aborted';
2795
+ }
2779
2796
  // Resolve any half-rendered TaskDisplay spinners before the prompt
2780
2797
  // comes back, otherwise the user sees `⟳ Execute tasks` next to `>`.
2781
2798
  try {
@@ -2807,30 +2824,32 @@ export class ChatCommand {
2807
2824
  workspaceSyncIssue: null,
2808
2825
  finishedAt: Date.now(),
2809
2826
  };
2827
+ if (!this.jsonOutput) {
2828
+ process.exitCode = 1;
2829
+ }
2810
2830
  if (this.jsonOutput) {
2811
2831
  process.exitCode = 1;
2812
2832
  console.log(JSON.stringify({
2813
2833
  success: false,
2814
2834
  mode: 'agent',
2815
2835
  model: routingPolicy.selectedModel,
2816
- partial: false,
2836
+ partial: liveOutcome.tasksSucceeded > 0 && liveOutcome.tasksTotal > 0,
2817
2837
  content: '',
2818
2838
  error: errorMessage,
2839
+ statusHeadline: evaluateExecutorSuccess(liveOutcome).statusHeadline,
2840
+ tasksSucceeded: liveOutcome.tasksSucceeded,
2841
+ tasksTotal: liveOutcome.tasksTotal,
2819
2842
  metadata: { executionPath: 'v3-agent' },
2820
2843
  }, null, 2));
2821
2844
  }
2822
2845
  else if (!this.directPromptMode) {
2823
2846
  const failedEval = evaluateExecutorSuccess({
2847
+ ...liveOutcome,
2824
2848
  executorFailed: true,
2825
- plannerError: liveOutcome.plannerError,
2826
2849
  executorError: liveOutcome.executorError || safeDetail,
2827
- tasksTotal: liveOutcome.tasksTotal,
2828
- tasksSucceeded: liveOutcome.tasksSucceeded,
2829
2850
  changedFileCount: 0,
2830
2851
  requiresWorkspaceChanges: this.taskRequiresWorkspaceChanges(prompt),
2831
2852
  workspaceHasOutput: this.api.hasAgentWorkspaceOutput(workspaceContext),
2832
- failedTaskIds: liveOutcome.failedTaskIds,
2833
- analysisToolsUsed: liveOutcome.analysisToolsUsed,
2834
2853
  });
2835
2854
  this.printAgentRunSummary(this.lastAgentRunOutcome, failedEval, 0);
2836
2855
  }
@@ -3312,16 +3331,15 @@ export class ChatCommand {
3312
3331
  }
3313
3332
  const o = this.lastAgentRunOutcome;
3314
3333
  const statusEval = evaluateExecutorSuccess({
3334
+ ...createLiveOutcome(),
3315
3335
  executorFailed: o.failedTaskIds.length > 0,
3316
3336
  plannerError: o.plannerError,
3317
3337
  executorError: o.executorError,
3318
3338
  tasksTotal: o.tasksTotal,
3319
3339
  tasksSucceeded: o.tasksSucceeded,
3320
- changedFileCount: 0,
3321
3340
  requiresWorkspaceChanges: this.taskRequiresWorkspaceChanges(o.prompt),
3322
3341
  workspaceHasOutput: o.hasOutput,
3323
3342
  failedTaskIds: new Set(o.failedTaskIds),
3324
- analysisToolsUsed: 0,
3325
3343
  });
3326
3344
  this.printAgentRunSummary(o, statusEval, 0);
3327
3345
  }
@@ -12,7 +12,9 @@ export interface LiveOutcome {
12
12
  requiresWorkspaceChanges: boolean;
13
13
  workspaceHasOutput: boolean;
14
14
  failedTaskIds: Set<string>;
15
+ completedTaskIds: Set<string>;
15
16
  analysisToolsUsed: number;
17
+ streamAborted?: boolean;
16
18
  }
17
19
  export interface TaskSummaryEvent {
18
20
  status?: string;
@@ -13,24 +13,36 @@ export function createLiveOutcome() {
13
13
  requiresWorkspaceChanges: false,
14
14
  workspaceHasOutput: false,
15
15
  failedTaskIds: new Set(),
16
+ completedTaskIds: new Set(),
16
17
  analysisToolsUsed: 0,
18
+ streamAborted: false,
17
19
  };
18
20
  }
19
21
  /** Process executor_complete / task summary events from the V3 SSE stream. */
20
22
  export function handleTaskEvent(summary, liveOutcome) {
21
23
  const tid = summary.task_id || summary.id;
22
24
  const status = String(summary.status || '').toLowerCase();
25
+ if (!liveOutcome.completedTaskIds) {
26
+ liveOutcome.completedTaskIds = new Set();
27
+ }
23
28
  if (status === 'completed' || status === 'success') {
24
29
  if (tid) {
25
- liveOutcome.failedTaskIds.delete(String(tid));
30
+ const id = String(tid);
31
+ if (liveOutcome.completedTaskIds.has(id))
32
+ return;
33
+ liveOutcome.completedTaskIds.add(id);
34
+ liveOutcome.failedTaskIds.delete(id);
26
35
  }
27
36
  liveOutcome.tasksSucceeded += 1;
28
37
  }
29
- else if (status === 'failed' || status === 'blocked') {
38
+ else if (status === 'failed' || status === 'blocked' || status === 'stalled_error') {
30
39
  if (tid) {
31
40
  liveOutcome.failedTaskIds.add(String(tid));
32
41
  }
33
42
  liveOutcome.executorFailed = true;
43
+ if (status === 'stalled_error') {
44
+ liveOutcome.streamAborted = true;
45
+ }
34
46
  }
35
47
  }
36
48
  const READ_DISCOVERY_TOOLS = new Set([
@@ -57,7 +69,18 @@ export function noteAnalysisToolUse(toolName, liveOutcome) {
57
69
  export function evaluateExecutorSuccess(liveOutcome) {
58
70
  const hasFatalError = liveOutcome.executorFailed
59
71
  || Boolean(liveOutcome.plannerError)
60
- || Boolean(liveOutcome.executorError);
72
+ || Boolean(liveOutcome.executorError)
73
+ || Boolean(liveOutcome.streamAborted);
74
+ if (liveOutcome.streamAborted && liveOutcome.tasksTotal > 0) {
75
+ const partial = liveOutcome.tasksSucceeded > 0;
76
+ return {
77
+ executorSucceeded: false,
78
+ statusHeadline: partial
79
+ ? `⚠ ${liveOutcome.tasksSucceeded}/${liveOutcome.tasksTotal} tasks completed — connection aborted`
80
+ : `✕ Connection aborted — 0/${liveOutcome.tasksTotal} tasks completed`,
81
+ uiTheme: 'warning',
82
+ };
83
+ }
61
84
  if (hasFatalError && liveOutcome.tasksTotal === 0) {
62
85
  return {
63
86
  executorSucceeded: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,7 +36,8 @@
36
36
  "start": "node dist/index.js",
37
37
  "dev": "ts-node src/index.ts",
38
38
  "test": "npm run test:cli",
39
- "test:cli": "npm run build && npm run precheck:services && node scripts/test-cli-suite.js",
39
+ "test:agent:outcome": "npm run build && node scripts/test-agent-run-outcome.js",
40
+ "test:cli": "npm run build && npm run precheck:services && node scripts/test-cli-suite.js && npm run test:agent:outcome",
40
41
  "test:regression": "npm run build && node scripts/test-regression-1.6.22.js",
41
42
  "test:agent:smoke": "npm run build && node scripts/test-agent-smoke.js",
42
43
  "test:agent:routing": "npm run build && node scripts/test-agent-routing-policy.js",
@@ -131,4 +131,7 @@ assert fb.get('reason') == 'governance-blocked-model', j
131
131
  print('[pass] governance fallback metadata')
132
132
  PY
133
133
 
134
+ echo "[outcome-truth] agent run outcome regression"
135
+ npm run test:agent:outcome
136
+
134
137
  echo "ALL NO-GO GATES PASSED"