vigthoria-cli 1.13.13 → 1.13.20
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/commands/auth.js +21 -7
- package/dist/commands/background.d.ts +3 -0
- package/dist/commands/background.js +58 -1
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +121 -1
- package/dist/commands/config.js +10 -14
- package/dist/commands/legion.js +48 -13
- package/dist/index.js +107 -6
- package/dist/utils/api.d.ts +7 -0
- package/dist/utils/api.js +236 -55
- package/dist/utils/clientManifest.d.ts +9 -0
- package/dist/utils/clientManifest.js +11 -1
- package/dist/utils/command-menu.js +6 -0
- package/dist/utils/config.js +7 -5
- package/dist/utils/contextBudget.d.ts +5 -1
- package/dist/utils/contextBudget.js +7 -4
- package/dist/utils/fastAgentRouter.d.ts +2 -2
- package/dist/utils/fastAgentRouter.js +4 -5
- package/dist/utils/requestIntent.js +5 -4
- package/dist/utils/session.d.ts +18 -0
- package/dist/utils/v3-workspace-path.js +22 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +3 -1
- package/scripts/release/validate-no-go-gates.sh +45 -7
package/dist/commands/auth.js
CHANGED
|
@@ -11,9 +11,13 @@ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
|
11
11
|
const KNOWN_AUTH_BASE_URLS = ['https://coder.vigthoria.io'];
|
|
12
12
|
class HttpError extends Error {
|
|
13
13
|
status;
|
|
14
|
-
|
|
14
|
+
code;
|
|
15
|
+
upgradeUrl;
|
|
16
|
+
constructor(status, message, code, upgradeUrl) {
|
|
15
17
|
super(message);
|
|
16
18
|
this.status = status;
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.upgradeUrl = upgradeUrl;
|
|
17
21
|
}
|
|
18
22
|
}
|
|
19
23
|
function trimTrailingSlash(value) {
|
|
@@ -206,12 +210,19 @@ async function requestJson(url, init) {
|
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
212
|
if (!response.ok) {
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
const errorBody = typeof body === 'object' && body ? body : {};
|
|
214
|
+
const code = String(errorBody.code || '');
|
|
215
|
+
const planNames = Array.isArray(errorBody.requiredPlans)
|
|
216
|
+
? errorBody.requiredPlans.map(plan => plan.name || plan.id).filter(Boolean)
|
|
217
|
+
: [];
|
|
218
|
+
const message = code === 'SUBSCRIPTION_REQUIRED'
|
|
219
|
+
? `This project requires a paid Vigthoria subscription.${planNames.length ? ` Eligible plans: ${planNames.join(', ')}.` : ''} Upgrade: ${errorBody.upgradeUrl || 'https://hub.vigthoria.io/subscriptions?application=coder'}`
|
|
220
|
+
: 'error' in errorBody && errorBody.error
|
|
221
|
+
? String(errorBody.error)
|
|
222
|
+
: 'message' in errorBody && errorBody.message
|
|
223
|
+
? String(errorBody.message)
|
|
224
|
+
: `Request failed with status ${response.status}`;
|
|
225
|
+
throw new HttpError(response.status, message, code || undefined, errorBody.upgradeUrl);
|
|
215
226
|
}
|
|
216
227
|
return body;
|
|
217
228
|
}
|
|
@@ -316,6 +327,9 @@ export async function login(email, password) {
|
|
|
316
327
|
return config;
|
|
317
328
|
}
|
|
318
329
|
catch (error) {
|
|
330
|
+
if (error instanceof HttpError && error.code === 'SUBSCRIPTION_REQUIRED') {
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
319
333
|
const message = humanMessage(error);
|
|
320
334
|
failures.push(`${endpoint} -> ${message}`);
|
|
321
335
|
}
|
|
@@ -4,6 +4,8 @@ interface BackgroundOptions {
|
|
|
4
4
|
json?: boolean;
|
|
5
5
|
limit?: number;
|
|
6
6
|
workspace?: string;
|
|
7
|
+
deny?: boolean;
|
|
8
|
+
model?: string;
|
|
7
9
|
}
|
|
8
10
|
export declare class BackgroundCommand {
|
|
9
11
|
private config;
|
|
@@ -16,5 +18,6 @@ export declare class BackgroundCommand {
|
|
|
16
18
|
apply(jobId: string, options?: BackgroundOptions): Promise<void>;
|
|
17
19
|
cancel(jobId: string, options?: BackgroundOptions): Promise<void>;
|
|
18
20
|
private extractBackgroundOutcome;
|
|
21
|
+
approve(jobId: string, approvalId: string, options?: BackgroundOptions): Promise<void>;
|
|
19
22
|
}
|
|
20
23
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { createSpinner, CH } from '../utils/logger.js';
|
|
3
3
|
import { APIClient } from '../utils/api.js';
|
|
4
|
+
import { buildExecutionHints, inferAgentTaskType } from '../utils/requestIntent.js';
|
|
4
5
|
export class BackgroundCommand {
|
|
5
6
|
config;
|
|
6
7
|
logger;
|
|
@@ -17,6 +18,15 @@ export class BackgroundCommand {
|
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
const workspacePath = options.workspace || process.cwd();
|
|
21
|
+
const requestedModel = String(options.model || 'agent').trim().toLowerCase();
|
|
22
|
+
const allowedModels = new Set(['agent', 'cloud', 'cloud-reason', 'ultra']);
|
|
23
|
+
if (!allowedModels.has(requestedModel)) {
|
|
24
|
+
this.logger.error('Background model must be one of: agent, cloud, cloud-reason, ultra');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const cloudSelected = ['cloud', 'cloud-reason', 'ultra'].includes(requestedModel);
|
|
28
|
+
const agentTaskType = inferAgentTaskType(prompt);
|
|
29
|
+
const routedHints = buildExecutionHints(agentTaskType, prompt);
|
|
20
30
|
const spinner = createSpinner('Starting background agent job...').start();
|
|
21
31
|
try {
|
|
22
32
|
const job = await this.api.startV3BackgroundJob(prompt, {
|
|
@@ -27,6 +37,19 @@ export class BackgroundCommand {
|
|
|
27
37
|
executionSurface: 'cli',
|
|
28
38
|
clientSurface: 'cli',
|
|
29
39
|
clientToolExecution: false,
|
|
40
|
+
model: requestedModel,
|
|
41
|
+
requestedModel,
|
|
42
|
+
agentTaskType,
|
|
43
|
+
workflowType: routedHints.workflow_type,
|
|
44
|
+
executionHints: {
|
|
45
|
+
...routedHints,
|
|
46
|
+
cloud_selected: cloudSelected,
|
|
47
|
+
// Detached jobs must preserve the selected transport. This stops a
|
|
48
|
+
// paid cloud request from being silently reinterpreted as a local
|
|
49
|
+
// router alias during recovery.
|
|
50
|
+
enforce_cloud_only: cloudSelected,
|
|
51
|
+
background_model: requestedModel,
|
|
52
|
+
},
|
|
30
53
|
});
|
|
31
54
|
spinner.stop();
|
|
32
55
|
if (options.json) {
|
|
@@ -37,6 +60,7 @@ export class BackgroundCommand {
|
|
|
37
60
|
console.log(chalk.gray(` Job: ${job.job_id}`));
|
|
38
61
|
console.log(chalk.gray(` Status: ${job.status}`));
|
|
39
62
|
console.log(chalk.gray(` Workspace: ${workspacePath}`));
|
|
63
|
+
console.log(chalk.gray(` Model: ${requestedModel}`));
|
|
40
64
|
console.log();
|
|
41
65
|
console.log(chalk.gray(`Use ${chalk.cyan(`vigthoria background status ${job.job_id}`)} to check progress.`));
|
|
42
66
|
console.log(chalk.gray(`Use ${chalk.cyan(`vigthoria background apply ${job.job_id}`)} when it is completed.`));
|
|
@@ -100,6 +124,14 @@ export class BackgroundCommand {
|
|
|
100
124
|
}
|
|
101
125
|
if (job.error)
|
|
102
126
|
console.log(chalk.red(`Error: ${job.error}`));
|
|
127
|
+
const approvals = Array.isArray(job.pending_approvals) ? job.pending_approvals : [];
|
|
128
|
+
if (approvals.length > 0) {
|
|
129
|
+
console.log(chalk.yellow(`Gate: ${approvals.length} command approval(s) required`));
|
|
130
|
+
for (const approval of approvals) {
|
|
131
|
+
console.log(chalk.gray(` ${approval.approval_id}: ${approval.command || '(empty command)'}`));
|
|
132
|
+
console.log(chalk.gray(` Resolve: vigthoria background approve ${job.job_id} ${approval.approval_id}`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
103
135
|
console.log(chalk.gray(`\n${String(job.request || '').slice(0, 500)}`));
|
|
104
136
|
if (job.status === 'completed' && (job.file_count || 0) > 0) {
|
|
105
137
|
console.log(chalk.gray(`\nUse ${chalk.cyan(`vigthoria background apply ${job.job_id}`)} to apply files locally.`));
|
|
@@ -170,6 +202,8 @@ export class BackgroundCommand {
|
|
|
170
202
|
let tasksCompleted = Number(result.tasks_completed ?? result.tasksCompleted ?? 0);
|
|
171
203
|
let tasksTotal = Number(result.tasks_total ?? result.tasksTotal ?? 0);
|
|
172
204
|
let success = typeof result.success === 'boolean' ? result.success : null;
|
|
205
|
+
if (result.type === 'error')
|
|
206
|
+
success = false;
|
|
173
207
|
for (const event of events) {
|
|
174
208
|
if (!event || typeof event !== 'object')
|
|
175
209
|
continue;
|
|
@@ -192,7 +226,7 @@ export class BackgroundCommand {
|
|
|
192
226
|
if (success == null && tasksTotal > 0) {
|
|
193
227
|
success = tasksCompleted >= tasksTotal;
|
|
194
228
|
}
|
|
195
|
-
if (success == null && job?.status === 'completed') {
|
|
229
|
+
if (success == null && job?.status === 'completed' && result.type !== 'error') {
|
|
196
230
|
success = true;
|
|
197
231
|
}
|
|
198
232
|
if (success == null && job?.status === 'failed') {
|
|
@@ -211,4 +245,27 @@ export class BackgroundCommand {
|
|
|
211
245
|
}
|
|
212
246
|
return { tasksCompleted, tasksTotal, success, headline };
|
|
213
247
|
}
|
|
248
|
+
async approve(jobId, approvalId, options = {}) {
|
|
249
|
+
if (!jobId || !approvalId) {
|
|
250
|
+
this.logger.error('Usage: vigthoria background approve <job-id> <approval-id>');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const approved = !options.deny;
|
|
254
|
+
const spinner = createSpinner(`${approved ? 'Approving' : 'Denying'} background command...`).start();
|
|
255
|
+
try {
|
|
256
|
+
const result = await this.api.resolveV3BackgroundApproval(jobId, approvalId, approved, 'once');
|
|
257
|
+
spinner.stop();
|
|
258
|
+
if (options.json) {
|
|
259
|
+
console.log(JSON.stringify({ ...result, job_id: jobId, approval_id: approvalId, approved }, null, 2));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
console.log(approved
|
|
263
|
+
? chalk.green(`${CH.success} Approved ${approvalId} once for ${jobId}`)
|
|
264
|
+
: chalk.yellow(`${CH.warn} Denied ${approvalId} for ${jobId}`));
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
spinner.stop();
|
|
268
|
+
this.logger.error(error?.message || String(error));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
214
271
|
}
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -272,6 +272,8 @@ export declare class ChatCommand {
|
|
|
272
272
|
private truncateText;
|
|
273
273
|
private getLastUserPrompt;
|
|
274
274
|
private saveSession;
|
|
275
|
+
private persistAgentExecutionCheckpoint;
|
|
276
|
+
private updateAgentExecutionCheckpoint;
|
|
275
277
|
private requestPermission;
|
|
276
278
|
getCurrentSessionInfo(): string;
|
|
277
279
|
getChatHistory(): ChatMessage[];
|
package/dist/commands/chat.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from 'fs';
|
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import * as readline from 'readline';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
6
7
|
import { createSpinner } from '../utils/logger.js';
|
|
7
8
|
import { APIClient, CLIError, classifyError, formatCLIError, sanitizeUserFacingErrorText, sanitizeUserFacingPathText, propagateError, VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE } from '../utils/api.js';
|
|
8
9
|
import { AgenticTools, robustifyStreamResponse } from '../utils/tools.js';
|
|
@@ -1179,6 +1180,32 @@ export class ChatCommand {
|
|
|
1179
1180
|
spinner.text = this.sanitizeServerPath(String(event.content || 'Routing to V3 Agent...'));
|
|
1180
1181
|
return;
|
|
1181
1182
|
}
|
|
1183
|
+
if (event.type === 'pipeline_stage') {
|
|
1184
|
+
if (spinner.isSpinning)
|
|
1185
|
+
spinner.stop();
|
|
1186
|
+
const stageIndex = Number.isFinite(Number(event.stage_index)) ? Number(event.stage_index) : 0;
|
|
1187
|
+
const stageTotal = Number.isFinite(Number(event.stage_total)) ? Number(event.stage_total) : 3;
|
|
1188
|
+
const stage = String(event.stage || '').trim().toLowerCase();
|
|
1189
|
+
const label = stage === 'dispatcher'
|
|
1190
|
+
? 'Dispatcher'
|
|
1191
|
+
: stage === 'architect'
|
|
1192
|
+
? 'Architect'
|
|
1193
|
+
: stage === 'executor'
|
|
1194
|
+
? 'Executor'
|
|
1195
|
+
: 'Pipeline';
|
|
1196
|
+
const model = this.sanitizeServerPath(String(event.model || event.executor_model || ''));
|
|
1197
|
+
const route = this.sanitizeServerPath(String(event.route || ''));
|
|
1198
|
+
const details = [model, route ? `route=${route}` : ''].filter(Boolean).join(' · ');
|
|
1199
|
+
process.stderr.write(chalk.cyan(` [${stageIndex || '?'}\/${stageTotal} ${label}] `)
|
|
1200
|
+
+ `${details || 'ready'}\n`);
|
|
1201
|
+
spinner.start();
|
|
1202
|
+
spinner.text = stage === 'dispatcher'
|
|
1203
|
+
? 'Olivia routed the request. Starting Architect...'
|
|
1204
|
+
: stage === 'architect'
|
|
1205
|
+
? 'Architect is producing the execution plan...'
|
|
1206
|
+
: '35B Executor is executing the approved plan...';
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1182
1209
|
if (event.type === 'thinking') {
|
|
1183
1210
|
this.v3IterationCount += 1;
|
|
1184
1211
|
const iterText = this.sanitizeServerPath(event.content || '');
|
|
@@ -1481,7 +1508,8 @@ export class ChatCommand {
|
|
|
1481
1508
|
this.sessionManager = new SessionManager();
|
|
1482
1509
|
}
|
|
1483
1510
|
async run(options) {
|
|
1484
|
-
|
|
1511
|
+
const hasRuntimeToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
1512
|
+
if (!this.config.isAuthenticated() && !hasRuntimeToken) {
|
|
1485
1513
|
if (options.json) {
|
|
1486
1514
|
process.exitCode = 1;
|
|
1487
1515
|
emitAgentJsonOutput({ success: false, error: 'Not authenticated. Run: vigthoria login' });
|
|
@@ -1811,6 +1839,35 @@ export class ChatCommand {
|
|
|
1811
1839
|
originalPrompt: saved.originalPrompt ?? null,
|
|
1812
1840
|
};
|
|
1813
1841
|
}
|
|
1842
|
+
else if (this.currentSession.activeAgentRun?.prompt) {
|
|
1843
|
+
const checkpoint = this.currentSession.activeAgentRun;
|
|
1844
|
+
this.lastAgentRunOutcome = {
|
|
1845
|
+
prompt: checkpoint.prompt,
|
|
1846
|
+
originalPrompt: checkpoint.originalPrompt || checkpoint.prompt,
|
|
1847
|
+
taskId: checkpoint.executionId,
|
|
1848
|
+
contextId: checkpoint.contextId,
|
|
1849
|
+
tasksSucceeded: 0,
|
|
1850
|
+
tasksTotal: 0,
|
|
1851
|
+
failedTaskIds: [...(checkpoint.failedTaskIds || [])],
|
|
1852
|
+
unfinishedTaskIds: checkpoint.unfinishedTaskIds?.length
|
|
1853
|
+
? [...checkpoint.unfinishedTaskIds]
|
|
1854
|
+
: ['resume-interrupted-agent-run'],
|
|
1855
|
+
qualityScore: null,
|
|
1856
|
+
qualityMissing: [],
|
|
1857
|
+
qualityBlockers: checkpoint.error ? [checkpoint.error] : [],
|
|
1858
|
+
hasOutput: false,
|
|
1859
|
+
answerContent: null,
|
|
1860
|
+
selfHealStatus: 'skipped',
|
|
1861
|
+
selfHealTool: null,
|
|
1862
|
+
plannerError: null,
|
|
1863
|
+
executorError: checkpoint.error || 'The previous client process ended before the agent run completed.',
|
|
1864
|
+
clientToolErrors: [],
|
|
1865
|
+
transportErrors: [],
|
|
1866
|
+
workspacePath: checkpoint.workspacePath || this.currentProjectPath,
|
|
1867
|
+
workspaceSyncIssue: null,
|
|
1868
|
+
finishedAt: Date.parse(checkpoint.updatedAt) || Date.now(),
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1814
1871
|
return;
|
|
1815
1872
|
}
|
|
1816
1873
|
}
|
|
@@ -2337,6 +2394,12 @@ export class ChatCommand {
|
|
|
2337
2394
|
: '';
|
|
2338
2395
|
this.logger.debug(`Agent route: ${r.path} / ${r.taskKind} [${r.source}]${routerNote} — ${r.reason}`);
|
|
2339
2396
|
}
|
|
2397
|
+
// V3 owns high-fidelity HTML and other complex single-file rewrites, but
|
|
2398
|
+
// the CLI still has the authoritative local workspace. Prime the target
|
|
2399
|
+
// through the client-tool bridge before handing the task to V3 so the
|
|
2400
|
+
// model receives the real current file and the terminal records a
|
|
2401
|
+
// verifiable read-before-write operation.
|
|
2402
|
+
await this.primeBypassedTargetFileContext(resolvedPrompt);
|
|
2340
2403
|
const handledByV3Workflow = await this.tryV3AgentWorkflow(resolvedPrompt);
|
|
2341
2404
|
if (handledByV3Workflow) {
|
|
2342
2405
|
this.saveSession();
|
|
@@ -3160,12 +3223,33 @@ export class ChatCommand {
|
|
|
3160
3223
|
...(resumeTaskIds.length > 0 ? { remaining_task_ids: resumeTaskIds } : {}),
|
|
3161
3224
|
}
|
|
3162
3225
|
: executionHints;
|
|
3226
|
+
const previousExecutionId = this.currentSession?.activeAgentRun?.executionId || null;
|
|
3227
|
+
const executionId = `cli-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
3163
3228
|
const workspaceContext = {
|
|
3164
3229
|
workspacePath: workspacePath,
|
|
3165
3230
|
projectPath: workspacePath,
|
|
3166
3231
|
targetPath: workspacePath,
|
|
3232
|
+
contextId: executionId,
|
|
3233
|
+
executionId,
|
|
3167
3234
|
...runtimeContext,
|
|
3168
3235
|
};
|
|
3236
|
+
this.persistAgentExecutionCheckpoint({
|
|
3237
|
+
executionId,
|
|
3238
|
+
parentExecutionId: shouldResumePlan ? previousExecutionId : null,
|
|
3239
|
+
contextId: executionId,
|
|
3240
|
+
prompt,
|
|
3241
|
+
originalPrompt: priorOutcome?.originalPrompt || priorOutcome?.prompt || prompt,
|
|
3242
|
+
workspacePath,
|
|
3243
|
+
userTier: String(this.config.get('subscription')?.plan || '').trim() || null,
|
|
3244
|
+
taskType: agentTaskType,
|
|
3245
|
+
workflowType,
|
|
3246
|
+
status: 'submitted',
|
|
3247
|
+
submittedAt: new Date().toISOString(),
|
|
3248
|
+
updatedAt: new Date().toISOString(),
|
|
3249
|
+
failedTaskIds: resumeTaskIds,
|
|
3250
|
+
unfinishedTaskIds: resumeTaskIds.length ? resumeTaskIds : ['agent-run-in-progress'],
|
|
3251
|
+
error: null,
|
|
3252
|
+
});
|
|
3169
3253
|
// Start workspace watcher for bidirectional real-time sync
|
|
3170
3254
|
let watcher = null;
|
|
3171
3255
|
if (this.shouldStartWorkspaceWatcher(workspacePath)) {
|
|
@@ -3209,6 +3293,7 @@ export class ChatCommand {
|
|
|
3209
3293
|
requestedModel: this.currentModel,
|
|
3210
3294
|
agentExecutionPolicy: routingPolicy,
|
|
3211
3295
|
legacyFallbackAllowed: this.isLegacyAgentFallbackAllowed(),
|
|
3296
|
+
approvalLevel: this.autoApprove ? 'auto' : 'confirm',
|
|
3212
3297
|
rawPrompt: prompt,
|
|
3213
3298
|
contextualPrompt,
|
|
3214
3299
|
history: this.getMessagesForModel(),
|
|
@@ -3218,6 +3303,7 @@ export class ChatCommand {
|
|
|
3218
3303
|
return;
|
|
3219
3304
|
}
|
|
3220
3305
|
if (event.type === 'plan') {
|
|
3306
|
+
this.updateAgentExecutionCheckpoint({ status: 'running' });
|
|
3221
3307
|
taskDisplay.complete(0);
|
|
3222
3308
|
const tasks = event?.plan?.tasks;
|
|
3223
3309
|
if (Array.isArray(tasks) && tasks.length > 0) {
|
|
@@ -3518,6 +3604,15 @@ export class ChatCommand {
|
|
|
3518
3604
|
: null,
|
|
3519
3605
|
finishedAt: Date.now(),
|
|
3520
3606
|
}, prompt);
|
|
3607
|
+
this.updateAgentExecutionCheckpoint({
|
|
3608
|
+
status: executorSucceeded ? 'completed' : 'failed',
|
|
3609
|
+
contextId: response.contextId || executionId,
|
|
3610
|
+
failedTaskIds: [...liveOutcome.failedTaskIds],
|
|
3611
|
+
unfinishedTaskIds: [...liveOutcome.unfinishedTaskIds],
|
|
3612
|
+
error: executorSucceeded
|
|
3613
|
+
? null
|
|
3614
|
+
: (liveOutcome.executorError || liveOutcome.plannerError || runEvaluation.statusHeadline),
|
|
3615
|
+
});
|
|
3521
3616
|
if (!this.jsonOutput && !this.directPromptMode) {
|
|
3522
3617
|
if (this.lastAgentRunOutcome) {
|
|
3523
3618
|
this.printAgentRunSummary(this.lastAgentRunOutcome, runEvaluation, changedFileCount);
|
|
@@ -3619,6 +3714,12 @@ export class ChatCommand {
|
|
|
3619
3714
|
workspaceSyncIssue: null,
|
|
3620
3715
|
finishedAt: Date.now(),
|
|
3621
3716
|
}, prompt);
|
|
3717
|
+
this.updateAgentExecutionCheckpoint({
|
|
3718
|
+
status: 'failed',
|
|
3719
|
+
failedTaskIds: [...liveOutcome.failedTaskIds],
|
|
3720
|
+
unfinishedTaskIds: [...liveOutcome.unfinishedTaskIds],
|
|
3721
|
+
error: liveOutcome.executorError || safeDetail || 'Connection aborted',
|
|
3722
|
+
});
|
|
3622
3723
|
if (!this.jsonOutput) {
|
|
3623
3724
|
process.exitCode = 1;
|
|
3624
3725
|
}
|
|
@@ -3688,6 +3789,7 @@ export class ChatCommand {
|
|
|
3688
3789
|
requestedModel: this.currentModel,
|
|
3689
3790
|
agentExecutionPolicy: routingPolicy,
|
|
3690
3791
|
legacyFallbackAllowed: this.isLegacyAgentFallbackAllowed(),
|
|
3792
|
+
approvalLevel: this.autoApprove ? 'auto' : 'confirm',
|
|
3691
3793
|
rawPrompt,
|
|
3692
3794
|
history: this.getMessagesForModel(),
|
|
3693
3795
|
onStreamEvent: spinner ? (event) => this.updateV3AgentSpinner(spinner, event) : undefined,
|
|
@@ -5021,6 +5123,24 @@ export class ChatCommand {
|
|
|
5021
5123
|
sessionSummary: this.currentSession.memorySummary || '',
|
|
5022
5124
|
});
|
|
5023
5125
|
}
|
|
5126
|
+
persistAgentExecutionCheckpoint(checkpoint) {
|
|
5127
|
+
if (!this.currentSession) {
|
|
5128
|
+
this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode, this.operatorMode);
|
|
5129
|
+
}
|
|
5130
|
+
this.currentSession.activeAgentRun = checkpoint;
|
|
5131
|
+
this.saveSession();
|
|
5132
|
+
}
|
|
5133
|
+
updateAgentExecutionCheckpoint(update) {
|
|
5134
|
+
if (!this.currentSession?.activeAgentRun) {
|
|
5135
|
+
return;
|
|
5136
|
+
}
|
|
5137
|
+
this.currentSession.activeAgentRun = {
|
|
5138
|
+
...this.currentSession.activeAgentRun,
|
|
5139
|
+
...update,
|
|
5140
|
+
updatedAt: new Date().toISOString(),
|
|
5141
|
+
};
|
|
5142
|
+
this.saveSession();
|
|
5143
|
+
}
|
|
5024
5144
|
async requestPermission(action) {
|
|
5025
5145
|
if (this.autoApprove) {
|
|
5026
5146
|
return true;
|
package/dist/commands/config.js
CHANGED
|
@@ -40,7 +40,7 @@ export class ConfigCommand {
|
|
|
40
40
|
console.log();
|
|
41
41
|
const cwd = process.cwd();
|
|
42
42
|
const configFile = path.join(cwd, '.vigthoria.json');
|
|
43
|
-
const allowedModels = new Set(['code', 'code-35b', 'code-9b', 'balanced', 'balanced-4b']);
|
|
43
|
+
const allowedModels = new Set(['architect', 'code', 'assistant', 'code-35b', 'code-9b', 'balanced', 'balanced-4b']);
|
|
44
44
|
const hasOverrides = Boolean(options.model ||
|
|
45
45
|
options.ignorePatterns !== undefined ||
|
|
46
46
|
options.autoApplyFixes !== undefined ||
|
|
@@ -71,16 +71,16 @@ export class ConfigCommand {
|
|
|
71
71
|
let settings;
|
|
72
72
|
if (nonInteractive) {
|
|
73
73
|
const profileDefaults = {
|
|
74
|
-
safe: { defaultModel: '
|
|
74
|
+
safe: { defaultModel: 'assistant', autoApplyFixes: false },
|
|
75
75
|
balanced: { defaultModel: 'code', autoApplyFixes: false },
|
|
76
|
-
fast: { defaultModel: '
|
|
76
|
+
fast: { defaultModel: 'assistant', autoApplyFixes: true },
|
|
77
77
|
};
|
|
78
78
|
const profile = options.profile && profileDefaults[options.profile] ? options.profile : 'balanced';
|
|
79
79
|
const defaults = profileDefaults[profile];
|
|
80
80
|
const model = options.model || defaults.defaultModel;
|
|
81
81
|
if (!allowedModels.has(model)) {
|
|
82
82
|
this.logger.error(`Invalid --model: ${model}`);
|
|
83
|
-
this.logger.info('Allowed values: code, code-35b, code-9b, balanced, balanced-4b');
|
|
83
|
+
this.logger.info('Allowed values: architect, code, assistant, code-35b, code-9b, balanced, balanced-4b');
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
86
|
settings = {
|
|
@@ -98,12 +98,9 @@ export class ConfigCommand {
|
|
|
98
98
|
message: 'Default AI model:',
|
|
99
99
|
choices: [
|
|
100
100
|
{ name: '═══ Code Models ═══', disabled: true },
|
|
101
|
-
{ name: 'Vigthoria
|
|
102
|
-
{ name: 'Vigthoria
|
|
103
|
-
{ name: 'Vigthoria
|
|
104
|
-
{ name: '═══ General Models ═══', disabled: true },
|
|
105
|
-
{ name: 'Vigthoria Master 7.6B - Balanced general model', value: 'balanced' },
|
|
106
|
-
{ name: 'Vigthoria v3 Balanced 4B - Efficient general purpose', value: 'balanced-4b' },
|
|
101
|
+
{ name: 'Vigthoria v4 Creative 27B - Architect and planning', value: 'architect' },
|
|
102
|
+
{ name: 'Vigthoria v4 Code 27B - Production executor', value: 'code' },
|
|
103
|
+
{ name: 'Vigthoria v4 Assistant 9B - Fast FIM and diagnostics', value: 'assistant' },
|
|
107
104
|
],
|
|
108
105
|
default: 'code',
|
|
109
106
|
},
|
|
@@ -294,10 +291,9 @@ export class ConfigCommand {
|
|
|
294
291
|
name: 'defaultModel',
|
|
295
292
|
message: 'Default AI model:',
|
|
296
293
|
choices: [
|
|
297
|
-
{ name: 'Vigthoria
|
|
298
|
-
{ name: 'Vigthoria
|
|
299
|
-
{ name: 'Vigthoria
|
|
300
|
-
{ name: 'Vigthoria v3 Balanced 4B', value: 'balanced-4b' },
|
|
294
|
+
{ name: 'Vigthoria v4 Creative 27B — Architect', value: 'architect' },
|
|
295
|
+
{ name: 'Vigthoria v4 Code 27B — Executor', value: 'code' },
|
|
296
|
+
{ name: 'Vigthoria v4 Assistant 9B — FIM and diagnostics', value: 'assistant' },
|
|
301
297
|
],
|
|
302
298
|
default: current.preferences.defaultModel,
|
|
303
299
|
},
|
package/dist/commands/legion.js
CHANGED
|
@@ -48,19 +48,31 @@ export class LegionCommand {
|
|
|
48
48
|
}
|
|
49
49
|
getHyperloopUrls() {
|
|
50
50
|
const urls = new Set();
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
const normalizeBase = (raw) => {
|
|
52
|
+
let base = String(raw || '').trim().replace(/\/$/, '');
|
|
53
|
+
base = base.replace(/\/api\/hyperloop\/(?:health|modules|execute)$/i, '/api/hyperloop');
|
|
54
|
+
if (base && !/\/api\/hyperloop$/i.test(base))
|
|
55
|
+
base += '/api/hyperloop';
|
|
56
|
+
return base;
|
|
57
|
+
};
|
|
58
|
+
// On-server checks must try loopback first. The former configured 10.0.0.2
|
|
59
|
+
// route could consume the entire timeout while the same node was healthy.
|
|
59
60
|
if (isServerRuntime()) {
|
|
60
61
|
for (const internal of buildServerHyperloopUrls()) {
|
|
61
|
-
|
|
62
|
+
const normalized = normalizeBase(internal);
|
|
63
|
+
if (normalized)
|
|
64
|
+
urls.add(normalized);
|
|
62
65
|
}
|
|
63
66
|
}
|
|
67
|
+
const envUrl = normalizeBase(process.env.VIGTHORIA_HYPERLOOP_URL || '');
|
|
68
|
+
if (envUrl)
|
|
69
|
+
urls.add(envUrl);
|
|
70
|
+
const configuredApiUrl = String(this.config.get('apiUrl') || '').trim().replace(/\/$/, '');
|
|
71
|
+
if (configuredApiUrl) {
|
|
72
|
+
const normalized = normalizeBase(configuredApiUrl);
|
|
73
|
+
if (normalized)
|
|
74
|
+
urls.add(normalized);
|
|
75
|
+
}
|
|
64
76
|
return Array.from(urls);
|
|
65
77
|
}
|
|
66
78
|
getHeaders() {
|
|
@@ -1495,19 +1507,42 @@ export class LegionCommand {
|
|
|
1495
1507
|
for (const baseUrl of this.getHyperloopUrls()) {
|
|
1496
1508
|
try {
|
|
1497
1509
|
const response = await fetch(`${baseUrl}/status`, {
|
|
1498
|
-
signal: AbortSignal.timeout(
|
|
1510
|
+
signal: AbortSignal.timeout(8000),
|
|
1499
1511
|
headers: this.getHeaders(),
|
|
1500
1512
|
});
|
|
1513
|
+
let data;
|
|
1514
|
+
let healthOnly = false;
|
|
1501
1515
|
if (!response.ok) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1516
|
+
// /status is authenticated, while the root /health endpoint is the
|
|
1517
|
+
// authoritative liveness probe. A CLI without the internal service
|
|
1518
|
+
// credential must still report a live node as online rather than
|
|
1519
|
+
// falling through to a stale private-network address.
|
|
1520
|
+
const parsed = new URL(baseUrl);
|
|
1521
|
+
const healthUrl = /^(?:127\.0\.0\.1|localhost|::1)$/i.test(parsed.hostname)
|
|
1522
|
+
? `${parsed.origin}/health`
|
|
1523
|
+
: `${baseUrl}/health`;
|
|
1524
|
+
const healthResponse = await fetch(healthUrl, {
|
|
1525
|
+
signal: AbortSignal.timeout(3000),
|
|
1526
|
+
headers: this.getHeaders(),
|
|
1527
|
+
});
|
|
1528
|
+
if (!healthResponse.ok) {
|
|
1529
|
+
lastError = `Legion status check at ${baseUrl} failed: ${response.status} ${describeUpstreamStatus(response.status)}`;
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
data = await this.readJsonResponse(healthResponse, `health check at ${healthUrl}`);
|
|
1533
|
+
healthOnly = true;
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
data = await this.readJsonResponse(response, `status check at ${baseUrl}`);
|
|
1504
1537
|
}
|
|
1505
|
-
const data = await this.readJsonResponse(response, `status check at ${baseUrl}`);
|
|
1506
1538
|
spinner.stop();
|
|
1507
1539
|
console.log();
|
|
1508
1540
|
console.log(chalk.bold.white(` ${CH.hLine.repeat(3)} Legion Infrastructure ${CH.hLine.repeat(37)}`));
|
|
1509
1541
|
console.log();
|
|
1510
1542
|
console.log(chalk.gray(` Hyper Loop: `) + chalk.green('online'));
|
|
1543
|
+
if (healthOnly) {
|
|
1544
|
+
console.log(chalk.gray(' Control plane: liveness verified; authenticated worker details not exposed'));
|
|
1545
|
+
}
|
|
1511
1546
|
if (data.workers || data.active_workers) {
|
|
1512
1547
|
const count = data.workers || data.active_workers;
|
|
1513
1548
|
console.log(chalk.gray(` Active workers: `) + chalk.white(String(typeof count === 'number' ? count : Object.keys(count).length)));
|