vigthoria-cli 1.10.51 → 1.10.55

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.
@@ -0,0 +1,19 @@
1
+ import { Config } from '../utils/config.js';
2
+ import { Logger } from '../utils/logger.js';
3
+ interface BackgroundOptions {
4
+ json?: boolean;
5
+ limit?: number;
6
+ workspace?: string;
7
+ }
8
+ export declare class BackgroundCommand {
9
+ private config;
10
+ private logger;
11
+ private api;
12
+ constructor(config: Config, logger: Logger);
13
+ start(promptParts: string[], options?: BackgroundOptions): Promise<void>;
14
+ list(options?: BackgroundOptions): Promise<void>;
15
+ status(jobId: string, options?: BackgroundOptions): Promise<void>;
16
+ apply(jobId: string, options?: BackgroundOptions): Promise<void>;
17
+ cancel(jobId: string, options?: BackgroundOptions): Promise<void>;
18
+ }
19
+ export {};
@@ -0,0 +1,160 @@
1
+ import chalk from 'chalk';
2
+ import { createSpinner, CH } from '../utils/logger.js';
3
+ import { APIClient } from '../utils/api.js';
4
+ export class BackgroundCommand {
5
+ config;
6
+ logger;
7
+ api;
8
+ constructor(config, logger) {
9
+ this.config = config;
10
+ this.logger = logger;
11
+ this.api = new APIClient(config, logger);
12
+ }
13
+ async start(promptParts, options = {}) {
14
+ const prompt = promptParts.join(' ').trim();
15
+ if (!prompt) {
16
+ this.logger.error('Usage: vigthoria background start "<task>"');
17
+ return;
18
+ }
19
+ const workspacePath = options.workspace || process.cwd();
20
+ const spinner = createSpinner('Starting background agent job...').start();
21
+ try {
22
+ const job = await this.api.startV3BackgroundJob(prompt, {
23
+ workspacePath,
24
+ projectPath: workspacePath,
25
+ targetPath: workspacePath,
26
+ localMachineCapable: true,
27
+ executionSurface: 'cli',
28
+ clientSurface: 'cli',
29
+ clientToolExecution: false,
30
+ });
31
+ spinner.stop();
32
+ if (options.json) {
33
+ console.log(JSON.stringify(job, null, 2));
34
+ return;
35
+ }
36
+ console.log(chalk.green(`${CH.success} Background job started`));
37
+ console.log(chalk.gray(` Job: ${job.job_id}`));
38
+ console.log(chalk.gray(` Status: ${job.status}`));
39
+ console.log(chalk.gray(` Workspace: ${workspacePath}`));
40
+ console.log();
41
+ console.log(chalk.gray(`Use ${chalk.cyan(`vigthoria background status ${job.job_id}`)} to check progress.`));
42
+ console.log(chalk.gray(`Use ${chalk.cyan(`vigthoria background apply ${job.job_id}`)} when it is completed.`));
43
+ }
44
+ catch (error) {
45
+ spinner.stop();
46
+ this.logger.error(error?.message || String(error));
47
+ }
48
+ }
49
+ async list(options = {}) {
50
+ const spinner = createSpinner('Loading background jobs...').start();
51
+ try {
52
+ const jobs = await this.api.listV3BackgroundJobs(options.limit || 20);
53
+ spinner.stop();
54
+ if (options.json) {
55
+ console.log(JSON.stringify({ jobs }, null, 2));
56
+ return;
57
+ }
58
+ if (jobs.length === 0) {
59
+ console.log(chalk.gray('No background jobs found.'));
60
+ return;
61
+ }
62
+ console.log(chalk.bold(`\n${CH.success} Background Jobs\n`));
63
+ for (const job of jobs) {
64
+ const color = job.status === 'completed' ? chalk.green : job.status === 'failed' ? chalk.red : job.status === 'cancelled' ? chalk.yellow : chalk.cyan;
65
+ console.log(`${color(String(job.status || 'unknown').padEnd(10))} ${chalk.cyan(job.job_id)} ${chalk.gray(job.updated_at || '')}`);
66
+ console.log(chalk.gray(` ${String(job.request || '').slice(0, 100)}`));
67
+ console.log(chalk.gray(` files: ${job.file_count || 0} workspace: ${job.local_workspace_path || job.workspace_name || '-'}`));
68
+ }
69
+ console.log();
70
+ }
71
+ catch (error) {
72
+ spinner.stop();
73
+ this.logger.error(error?.message || String(error));
74
+ }
75
+ }
76
+ async status(jobId, options = {}) {
77
+ if (!jobId) {
78
+ this.logger.error('Usage: vigthoria background status <job-id>');
79
+ return;
80
+ }
81
+ const spinner = createSpinner('Loading background job...').start();
82
+ try {
83
+ const job = await this.api.getV3BackgroundJob(jobId);
84
+ spinner.stop();
85
+ if (options.json) {
86
+ console.log(JSON.stringify(job, null, 2));
87
+ return;
88
+ }
89
+ console.log(chalk.bold(`\nBackground Job ${chalk.cyan(job.job_id)}\n`));
90
+ console.log(`Status: ${job.status}`);
91
+ console.log(`Files: ${job.file_count || 0}`);
92
+ console.log(`Updated: ${job.updated_at || '-'}`);
93
+ console.log(`Workspace: ${job.local_workspace_path || job.workspace_name || '-'}`);
94
+ if (job.error)
95
+ console.log(chalk.red(`Error: ${job.error}`));
96
+ console.log(chalk.gray(`\n${String(job.request || '').slice(0, 500)}`));
97
+ if (job.status === 'completed' && (job.file_count || 0) > 0) {
98
+ console.log(chalk.gray(`\nUse ${chalk.cyan(`vigthoria background apply ${job.job_id}`)} to apply files locally.`));
99
+ }
100
+ }
101
+ catch (error) {
102
+ spinner.stop();
103
+ this.logger.error(error?.message || String(error));
104
+ }
105
+ }
106
+ async apply(jobId, options = {}) {
107
+ if (!jobId) {
108
+ this.logger.error('Usage: vigthoria background apply <job-id>');
109
+ return;
110
+ }
111
+ const workspacePath = options.workspace || process.cwd();
112
+ const spinner = createSpinner('Applying background job files...').start();
113
+ try {
114
+ const result = await this.api.applyV3BackgroundJobFiles(jobId, {
115
+ workspacePath,
116
+ projectPath: workspacePath,
117
+ targetPath: workspacePath,
118
+ });
119
+ spinner.stop();
120
+ if (options.json) {
121
+ console.log(JSON.stringify(result, null, 2));
122
+ return;
123
+ }
124
+ console.log(chalk.green(`${CH.success} Applied ${result.applied.length} file(s) from ${jobId}`));
125
+ for (const file of result.applied.slice(0, 20)) {
126
+ console.log(chalk.gray(` + ${file}`));
127
+ }
128
+ if (result.applied.length > 20) {
129
+ console.log(chalk.gray(` ... and ${result.applied.length - 20} more`));
130
+ }
131
+ if (result.skipped.length > 0) {
132
+ console.log(chalk.yellow(`Skipped ${result.skipped.length} unchanged or unsafe file(s).`));
133
+ }
134
+ }
135
+ catch (error) {
136
+ spinner.stop();
137
+ this.logger.error(error?.message || String(error));
138
+ }
139
+ }
140
+ async cancel(jobId, options = {}) {
141
+ if (!jobId) {
142
+ this.logger.error('Usage: vigthoria background cancel <job-id>');
143
+ return;
144
+ }
145
+ const spinner = createSpinner('Cancelling background job...').start();
146
+ try {
147
+ const result = await this.api.cancelV3BackgroundJob(jobId);
148
+ spinner.stop();
149
+ if (options.json) {
150
+ console.log(JSON.stringify(result, null, 2));
151
+ return;
152
+ }
153
+ console.log(chalk.yellow(`${CH.warn} Background job cancelled: ${jobId}`));
154
+ }
155
+ catch (error) {
156
+ spinner.stop();
157
+ this.logger.error(error?.message || String(error));
158
+ }
159
+ }
160
+ }
@@ -746,6 +746,7 @@ export class ChatCommand {
746
746
  .replace(/^\s*(?:json\s*)?\[\s*\{[\s\S]*?"tool"[\s\S]*$/gim, '')
747
747
  .replace(/^\s*(?:list_dir|read_file|write_file|edit_file|glob|grep|bash)\s*$/gim, '')
748
748
  .replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, '')
749
+ .replace(/<tool_code>[\s\S]*?<\/tool_code>/gi, '')
749
750
  .replace(/\n{3,}/g, '\n\n');
750
751
  return output;
751
752
  }
@@ -2491,6 +2492,9 @@ export class ChatCommand {
2491
2492
  if (tid)
2492
2493
  liveOutcome.failedTaskIds.add(String(tid));
2493
2494
  liveOutcome.executorFailed = true;
2495
+ const err = typeof summary.error === 'string' ? summary.error.trim() : '';
2496
+ if (err && !liveOutcome.executorError)
2497
+ liveOutcome.executorError = err;
2494
2498
  }
2495
2499
  else if (summary.status === 'completed' || summary.status === 'success') {
2496
2500
  if (tid)
@@ -2635,7 +2639,20 @@ export class ChatCommand {
2635
2639
  && !liveOutcome.executorError
2636
2640
  && (requiresWorkspaceChanges ? (workspaceHasOutput && changedFileCount > 0) : true);
2637
2641
  if (!executorSucceeded && requiresWorkspaceChanges && changedFileCount === 0 && !liveOutcome.executorError) {
2638
- liveOutcome.executorError = 'No workspace files were changed for a build/edit request.';
2642
+ const clientToolErrors = this.api.getClientToolErrors();
2643
+ const transportErrors = this.api.getLastChatTransportErrors();
2644
+ if (clientToolErrors.length > 0) {
2645
+ liveOutcome.executorError = clientToolErrors[0];
2646
+ }
2647
+ else if (workspaceHasOutput && changedFileCount === 0) {
2648
+ liveOutcome.executorError = 'Files may exist locally but were not tracked by the agent stream. Try /retry or check your workspace path.';
2649
+ }
2650
+ else {
2651
+ liveOutcome.executorError = 'No workspace files were changed for a build/edit request.';
2652
+ }
2653
+ if (transportErrors.length > 0 && !liveOutcome.plannerError) {
2654
+ liveOutcome.plannerError = transportErrors[0];
2655
+ }
2639
2656
  }
2640
2657
  if (executorSucceeded) {
2641
2658
  taskDisplay.complete(1);
@@ -2708,6 +2725,12 @@ export class ChatCommand {
2708
2725
  selfHealTool,
2709
2726
  plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
2710
2727
  executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : null,
2728
+ clientToolErrors: this.api.getClientToolErrors(),
2729
+ transportErrors: this.api.getLastChatTransportErrors(),
2730
+ workspacePath: workspacePath || null,
2731
+ workspaceSyncIssue: (!executorSucceeded && workspaceHasOutput && changedFileCount === 0)
2732
+ ? 'Local workspace has files but the agent did not confirm tracked changes.'
2733
+ : null,
2711
2734
  finishedAt: Date.now(),
2712
2735
  };
2713
2736
  if (!this.jsonOutput && !this.directPromptMode) {
@@ -2767,6 +2790,10 @@ export class ChatCommand {
2767
2790
  selfHealTool: null,
2768
2791
  plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
2769
2792
  executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : safeDetail || null,
2793
+ clientToolErrors: this.api.getClientToolErrors(),
2794
+ transportErrors: this.api.getLastChatTransportErrors(),
2795
+ workspacePath: workspacePath || null,
2796
+ workspaceSyncIssue: null,
2770
2797
  finishedAt: Date.now(),
2771
2798
  };
2772
2799
  if (this.jsonOutput) {
@@ -3129,7 +3156,29 @@ export class ChatCommand {
3129
3156
  console.log(chalk.gray(' Planner: ') + chalk.red(outcome.plannerError.slice(0, 140)));
3130
3157
  }
3131
3158
  if (outcome.executorError) {
3132
- console.log(chalk.gray(' Executor: ') + chalk.red(outcome.executorError.slice(0, 140)));
3159
+ console.log(chalk.gray(' Executor: ') + chalk.red(outcome.executorError.slice(0, 240)));
3160
+ }
3161
+ const hasDiagnostics = outcome.clientToolErrors.length > 0
3162
+ || outcome.transportErrors.length > 0
3163
+ || outcome.workspaceSyncIssue;
3164
+ if (hasDiagnostics) {
3165
+ console.log('');
3166
+ console.log(chalk.gray('Why this run struggled:'));
3167
+ if (outcome.workspacePath) {
3168
+ console.log(' ' + chalk.white(`Workspace: ${outcome.workspacePath}`));
3169
+ }
3170
+ for (const err of outcome.clientToolErrors.slice(0, 3)) {
3171
+ console.log(' ' + chalk.yellow('Local tools: ') + chalk.white(err.slice(0, 220)));
3172
+ }
3173
+ for (const err of outcome.transportErrors.slice(0, 2)) {
3174
+ console.log(' ' + chalk.yellow('Connectivity: ') + chalk.white(err.slice(0, 220)));
3175
+ }
3176
+ if (outcome.workspaceSyncIssue) {
3177
+ console.log(' ' + chalk.yellow('Sync: ') + chalk.white(outcome.workspaceSyncIssue));
3178
+ }
3179
+ if (outcome.clientToolErrors.some((e) => /no pending client tool call|rejected/i.test(e))) {
3180
+ console.log(' ' + chalk.gray('The agent tried to use your local machine for file tools, but the server timed out waiting. /retry usually works after the SSE bridge fix.'));
3181
+ }
3133
3182
  }
3134
3183
  console.log('');
3135
3184
  console.log(chalk.gray('Result:'));
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ import { HistoryCommand } from './commands/history.js';
35
35
  import { ReplayCommand } from './commands/replay.js';
36
36
  import { ForkCommand } from './commands/fork.js';
37
37
  import { CancelCommand } from './commands/cancel.js';
38
+ import { BackgroundCommand } from './commands/background.js';
38
39
  import { SecurityCommand } from './commands/security.js';
39
40
  import { Config } from './utils/config.js';
40
41
  import { Logger, CH } from './utils/logger.js';
@@ -333,7 +334,7 @@ function isAuthProtectedCommand(command) {
333
334
  'agent', 'a', 'operator', 'op',
334
335
  'edit', 'e', 'generate', 'g', 'explain', 'x', 'fix', 'f', 'review', 'r',
335
336
  'workflow', 'flow', 'hub', 'marketplace', 'deploy', 'host', 'preview', 'device',
336
- 'legion', 'history', 'runs', 'replay', 'fork', 'cancel',
337
+ 'legion', 'history', 'runs', 'replay', 'fork', 'cancel', 'background', 'bg',
337
338
  'repo', 'repository',
338
339
  ]);
339
340
  return protectedCommands.has(command);
@@ -489,7 +490,7 @@ export async function main(args) {
489
490
  const isLegionCortexRequest = invokedBinaryName === 'vigthoria' && argv[2] === 'legion' && argv.includes('--cortex');
490
491
  if (invokedBinaryName === 'vigthoria-chat') {
491
492
  const knownCommands = new Set([
492
- 'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel',
493
+ 'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel', 'background', 'bg',
493
494
  'hub', 'repo', 'deploy', 'operator', 'workflow', 'flow', 'device', 'security', 'vsec', 'login', 'logout', 'status', 'config', 'update',
494
495
  '--help', '-h', '--version', '-V', 'help', 'version',
495
496
  ]);
@@ -1088,6 +1089,58 @@ Examples:
1088
1089
  repoCommand.action(() => {
1089
1090
  repoCommand.outputHelp();
1090
1091
  });
1092
+ // ==================== BACKGROUND AGENT JOBS ====================
1093
+ const backgroundCommand = program
1094
+ .command('background')
1095
+ .alias('bg')
1096
+ .description('Run and manage detached Vigthoria Agent jobs');
1097
+ backgroundCommand
1098
+ .command('start <prompt...>')
1099
+ .description('Start an Agent job in the background and return immediately')
1100
+ .option('--workspace <path>', 'Local workspace path', process.cwd())
1101
+ .option('--json', 'Emit JSON output', false)
1102
+ .action(async (prompt, options) => {
1103
+ const bg = new BackgroundCommand(config, logger);
1104
+ await bg.start(prompt, options);
1105
+ });
1106
+ backgroundCommand
1107
+ .command('list')
1108
+ .alias('ls')
1109
+ .description('List recent background jobs')
1110
+ .option('--limit <n>', 'Maximum jobs to show', '20')
1111
+ .option('--json', 'Emit JSON output', false)
1112
+ .action(async (options) => {
1113
+ const bg = new BackgroundCommand(config, logger);
1114
+ await bg.list({ ...options, limit: Number(options.limit) || 20 });
1115
+ });
1116
+ backgroundCommand
1117
+ .command('status <jobId>')
1118
+ .description('Show background job status')
1119
+ .option('--json', 'Emit JSON output', false)
1120
+ .action(async (jobId, options) => {
1121
+ const bg = new BackgroundCommand(config, logger);
1122
+ await bg.status(jobId, options);
1123
+ });
1124
+ backgroundCommand
1125
+ .command('apply <jobId>')
1126
+ .description('Apply completed background job file changes to the local workspace')
1127
+ .option('--workspace <path>', 'Local workspace path', process.cwd())
1128
+ .option('--json', 'Emit JSON output', false)
1129
+ .action(async (jobId, options) => {
1130
+ const bg = new BackgroundCommand(config, logger);
1131
+ await bg.apply(jobId, options);
1132
+ });
1133
+ backgroundCommand
1134
+ .command('cancel <jobId>')
1135
+ .description('Cancel a running background job')
1136
+ .option('--json', 'Emit JSON output', false)
1137
+ .action(async (jobId, options) => {
1138
+ const bg = new BackgroundCommand(config, logger);
1139
+ await bg.cancel(jobId, options);
1140
+ });
1141
+ backgroundCommand.action(() => {
1142
+ backgroundCommand.outputHelp();
1143
+ });
1091
1144
  // ==================== DEPLOY COMMANDS ====================
1092
1145
  // Deploy command - Host projects on Vigthoria
1093
1146
  const deployCommand = program
@@ -224,6 +224,11 @@ export declare class APIClient {
224
224
  private vigFlowTokens;
225
225
  private _httpsAgent;
226
226
  private lastChatTransportErrors;
227
+ private clientToolErrors;
228
+ private pendingV3ClientToolTasks;
229
+ /** Tracks files mutated via client_tool_request during an active V3 SSE stream. */
230
+ private activeV3StreamedFiles;
231
+ private activeV3StreamContext;
227
232
  constructor(config: Config, logger: Logger);
228
233
  /**
229
234
  * Destroy keep-alive sockets so the Node.js event loop can drain
@@ -253,6 +258,7 @@ export declare class APIClient {
253
258
  getV3AgentBaseUrls(preferLocal?: boolean): string[];
254
259
  getV3AgentRunUrl(baseUrl: string): string;
255
260
  getV3AgentContinueUrl(baseUrl: string): string;
261
+ getV3AgentBackgroundUrl(baseUrl: string, suffix?: string): string;
256
262
  getOperatorBaseUrls(): string[];
257
263
  getOperatorStreamUrl(baseUrl: string): string;
258
264
  getMcpBaseUrls(): string[];
@@ -355,6 +361,10 @@ export declare class APIClient {
355
361
  extractExpectedWorkspaceFiles(message?: string, context?: Record<string, any>): string[];
356
362
  captureV3AgentStreamMutation(event: any, streamedFiles: Record<string, string>, serverRoot?: string | null): void;
357
363
  private applyV3AgentStreamEventToWorkspace;
364
+ private resolveV3ClientToolPath;
365
+ private recordV3ClientToolMutation;
366
+ private executeV3ClientToolRequest;
367
+ private handleV3ClientToolRequest;
358
368
  private writeV3AgentWorkspaceFile;
359
369
  private deleteV3AgentWorkspaceFile;
360
370
  recoverAgentWorkspaceFiles(context?: Record<string, any>, streamedFiles?: Record<string, string>, expectedFiles?: string[]): void;
@@ -374,8 +384,25 @@ export declare class APIClient {
374
384
  */
375
385
  private synthesizeAnswerFromV3Events;
376
386
  private sanitizeV3AgentEventForUser;
387
+ private resolveAgentChangedFiles;
388
+ private finalizeV3AgentWorkflowResponse;
377
389
  collectV3AgentStream(response: Response, context?: Record<string, any>): Promise<any>;
378
390
  runV3AgentWorkflow(message: string, context?: Record<string, any>): Promise<V3AgentWorkflowResponse>;
391
+ startV3BackgroundJob(message: string, context?: Record<string, any>): Promise<any>;
392
+ listV3BackgroundJobs(limit?: number): Promise<any[]>;
393
+ getV3BackgroundJob(jobId: string): Promise<any>;
394
+ cancelV3BackgroundJob(jobId: string): Promise<any>;
395
+ getV3BackgroundJobFiles(jobId: string): Promise<{
396
+ status: string;
397
+ files: Record<string, string>;
398
+ file_count: number;
399
+ local_workspace_path?: string;
400
+ }>;
401
+ applyV3BackgroundJobFiles(jobId: string, context?: Record<string, any>): Promise<{
402
+ applied: string[];
403
+ skipped: string[];
404
+ status: string;
405
+ }>;
379
406
  private formatOperatorResponse;
380
407
  runOperatorWorkflow(message: string, context?: Record<string, any>): Promise<OperatorWorkflowResponse>;
381
408
  /**
@@ -515,6 +542,9 @@ export declare class APIClient {
515
542
  }>;
516
543
  mapPreflightEndpointToRoute(endpoint: string): ChatRoutePreference | null;
517
544
  getLastChatTransportErrors(): string[];
545
+ getClientToolErrors(): string[];
546
+ clearAgentRunDiagnostics(): void;
547
+ private recordClientToolError;
518
548
  submitClientToolResult(contextId: string, callId: string, result: {
519
549
  success: boolean;
520
550
  output: string;