vigthoria-cli 1.8.19 → 1.9.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.
@@ -38,6 +38,15 @@ export declare class ChatCommand {
38
38
  private workflowTarget;
39
39
  private savePlanToVigFlow;
40
40
  private jsonOutput;
41
+ private modelGovernanceFallback;
42
+ private isJwtExpirationError;
43
+ private isNetworkError;
44
+ private isTimeoutError;
45
+ private toUserFacingApiError;
46
+ private handleApiError;
47
+ private callApi;
48
+ private callSyncApi;
49
+ private applyNoAgentGovernance;
41
50
  private syncInteractiveModeModel;
42
51
  private hasOperatorAccess;
43
52
  private operatorAccessMessage;
@@ -48,6 +48,7 @@ const tools_js_1 = require("../utils/tools.js");
48
48
  const session_js_1 = require("../utils/session.js");
49
49
  const bridge_client_js_1 = require("../utils/bridge-client.js");
50
50
  const workspace_stream_js_1 = require("../utils/workspace-stream.js");
51
+ const task_display_js_1 = require("../utils/task-display.js");
51
52
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
52
53
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
53
54
  if (!rawValue) {
@@ -65,12 +66,9 @@ const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
65
66
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
66
67
  })();
67
68
  const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
68
- const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS;
69
- if (!rawValue) {
70
- return 0;
71
- }
69
+ const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS || '300000';
72
70
  const parsed = Number.parseInt(rawValue, 10);
73
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
71
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
74
72
  })();
75
73
  class ChatCommand {
76
74
  config;
@@ -94,6 +92,140 @@ class ChatCommand {
94
92
  workflowTarget = null;
95
93
  savePlanToVigFlow = false;
96
94
  jsonOutput = false;
95
+ modelGovernanceFallback = null;
96
+ isJwtExpirationError(error) {
97
+ const candidate = error;
98
+ const message = `${candidate?.message || ''} ${typeof candidate?.details === 'string' ? candidate.details : ''}`.toLowerCase();
99
+ const code = String(candidate?.code || '').toLowerCase();
100
+ return candidate?.status === 401 && (code.includes('token_expired') ||
101
+ code.includes('jwt_expired') ||
102
+ message.includes('jwt expired') ||
103
+ message.includes('token expired') ||
104
+ message.includes('expired token') ||
105
+ message.includes('session expired'));
106
+ }
107
+ isNetworkError(error) {
108
+ const candidate = error;
109
+ const code = String(candidate?.code || candidate?.cause?.code || '').toUpperCase();
110
+ const name = String(candidate?.name || candidate?.cause?.name || '').toLowerCase();
111
+ const message = String(candidate?.message || candidate?.cause?.message || '').toLowerCase();
112
+ return [
113
+ 'ECONNRESET',
114
+ 'ECONNREFUSED',
115
+ 'EHOSTUNREACH',
116
+ 'ENETUNREACH',
117
+ 'ENOTFOUND',
118
+ 'EAI_AGAIN',
119
+ 'UND_ERR_CONNECT_TIMEOUT',
120
+ 'UND_ERR_HEADERS_TIMEOUT',
121
+ 'UND_ERR_BODY_TIMEOUT',
122
+ ].includes(code) ||
123
+ name.includes('fetcherror') ||
124
+ message.includes('network') ||
125
+ message.includes('fetch failed') ||
126
+ message.includes('socket') ||
127
+ message.includes('connection');
128
+ }
129
+ isTimeoutError(error) {
130
+ const candidate = error;
131
+ const code = String(candidate?.code || candidate?.cause?.code || '').toUpperCase();
132
+ const name = String(candidate?.name || candidate?.cause?.name || '').toLowerCase();
133
+ const message = String(candidate?.message || candidate?.cause?.message || '').toLowerCase();
134
+ return code.includes('TIMEOUT') ||
135
+ code === 'ETIMEDOUT' ||
136
+ name.includes('timeout') ||
137
+ name === 'aborterror' ||
138
+ message.includes('timed out') ||
139
+ message.includes('timeout') ||
140
+ message.includes('aborted');
141
+ }
142
+ toUserFacingApiError(error, context) {
143
+ const classified = (0, api_js_1.classifyError)(error);
144
+ const status = classified.statusCode || (this.isJwtExpirationError(error) ? 401 : 500);
145
+ if (this.isJwtExpirationError(error)) {
146
+ return new api_js_1.CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
147
+ }
148
+ if (this.isTimeoutError(error)) {
149
+ return new api_js_1.CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
150
+ }
151
+ if (this.isNetworkError(error)) {
152
+ return new api_js_1.CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
153
+ }
154
+ const message = (0, api_js_1.sanitizeUserFacingErrorText)(classified.message || `${context} failed`);
155
+ return new api_js_1.CLIError(message, status === 401 ? 'auth' : 'model_backend', { statusCode: status });
156
+ }
157
+ handleApiError(error, context) {
158
+ const userFacingError = this.toUserFacingApiError(error, context);
159
+ if (!this.jsonOutput) {
160
+ console.error(chalk_1.default.red(`${context} failed: ${userFacingError.message}`));
161
+ }
162
+ const original = error && typeof error === 'object' ? error : { message: String(error) };
163
+ (0, api_js_1.propagateError)({
164
+ ...original,
165
+ message: userFacingError.message,
166
+ statusCode: userFacingError.statusCode,
167
+ commandName: 'chat',
168
+ endpoint: original.endpoint || original?.config?.url || original?.details?.endpoint || context,
169
+ details: {
170
+ ...(original.details && typeof original.details === 'object' ? original.details : {}),
171
+ command: 'chat',
172
+ endpoint: original.endpoint || original?.config?.url || original?.details?.endpoint || context,
173
+ context,
174
+ },
175
+ });
176
+ }
177
+ async callApi(context, operation, retries = 1) {
178
+ let lastError;
179
+ for (let attempt = 0; attempt <= retries; attempt += 1) {
180
+ try {
181
+ return await operation();
182
+ }
183
+ catch (error) {
184
+ if (!this.jsonOutput) {
185
+ const transient = this.isTimeoutError(error) ? 'timeout' : this.isNetworkError(error) ? 'network error' : 'API error';
186
+ console.error(chalk_1.default.red(`${context} failed with ${transient}: ${this.toUserFacingApiError(error, context).message}`));
187
+ }
188
+ lastError = error;
189
+ if (this.isJwtExpirationError(error)) {
190
+ this.handleApiError(error, context);
191
+ }
192
+ if (attempt >= retries || (!this.isNetworkError(error) && !this.isTimeoutError(error))) {
193
+ this.handleApiError(error, context);
194
+ }
195
+ this.logger.warn(`${context} failed due to ${this.isTimeoutError(error) ? 'timeout' : 'network error'}; retrying (${attempt + 1}/${retries})...`);
196
+ await new Promise((resolve) => setTimeout(resolve, 500 * (attempt + 1)));
197
+ }
198
+ }
199
+ this.handleApiError(lastError, context);
200
+ }
201
+ callSyncApi(context, operation) {
202
+ try {
203
+ return operation();
204
+ }
205
+ catch (error) {
206
+ this.handleApiError(error, context);
207
+ }
208
+ }
209
+ applyNoAgentGovernance(requestedModel) {
210
+ const requested = String(requestedModel || '').trim().toLowerCase();
211
+ if (!requested || this.agentMode || this.operatorMode) {
212
+ this.modelGovernanceFallback = null;
213
+ return;
214
+ }
215
+ const blocked = new Set(['fast', 'mini', 'creative', 'creative-v3', 'creative-v4']);
216
+ if (blocked.has(requested)) {
217
+ const effectiveModel = 'code-35b';
218
+ this.modelGovernanceFallback = {
219
+ requestedModel,
220
+ effectiveModel,
221
+ reason: 'governance-blocked-model',
222
+ };
223
+ this.currentModel = effectiveModel;
224
+ this.logger.warn(`Model ${requestedModel} is not permitted for no-agent chat; using ${effectiveModel}`);
225
+ return;
226
+ }
227
+ this.modelGovernanceFallback = null;
228
+ }
97
229
  syncInteractiveModeModel(enabledMode) {
98
230
  if (enabledMode === 'agent') {
99
231
  if (!this.modelExplicitlySelected || this.currentModel === 'code' || !this.currentModel) {
@@ -102,12 +234,12 @@ class ChatCommand {
102
234
  return;
103
235
  }
104
236
  if (enabledMode === 'operator') {
105
- if (this.currentModel === 'agent' || this.currentModel === 'code-8b' || !this.currentModel) {
237
+ if (this.currentModel === 'agent' || this.currentModel === 'code-8b' || this.currentModel === 'code-9b' || !this.currentModel) {
106
238
  this.currentModel = 'code';
107
239
  }
108
240
  return;
109
241
  }
110
- if (this.currentModel === 'agent' || this.currentModel === 'code-8b' || !this.currentModel) {
242
+ if (this.currentModel === 'agent' || this.currentModel === 'code-8b' || this.currentModel === 'code-9b' || !this.currentModel) {
111
243
  this.currentModel = 'code';
112
244
  }
113
245
  }
@@ -128,7 +260,7 @@ class ChatCommand {
128
260
  resolveInitialModel(options) {
129
261
  const requestedModel = String(options.model || '').trim();
130
262
  if (requestedModel) {
131
- return options.operator === true && requestedModel === 'code-8b'
263
+ return options.operator === true && (requestedModel === 'code-8b' || requestedModel === 'code-9b')
132
264
  ? 'code'
133
265
  : requestedModel;
134
266
  }
@@ -297,7 +429,7 @@ class ChatCommand {
297
429
  if (!this.isBrowserTaskPrompt(prompt)) {
298
430
  return {};
299
431
  }
300
- const bridgeStatus = await this.api.getDevtoolsBridgeStatus();
432
+ const bridgeStatus = await this.callApi('Checking DevTools Bridge status', () => this.api.getDevtoolsBridgeStatus(), 0);
301
433
  if (!this.jsonOutput && bridgeStatus.ok) {
302
434
  console.log(chalk_1.default.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
303
435
  }
@@ -659,6 +791,7 @@ class ChatCommand {
659
791
  this.autoApprove = options.autoApprove === true || this.jsonOutput;
660
792
  this.modelExplicitlySelected = Boolean(String(options.model || '').trim());
661
793
  this.currentModel = this.resolveInitialModel(options);
794
+ this.applyNoAgentGovernance(String(options.model || this.currentModel || ''));
662
795
  this.currentProjectPath = this.resolveProjectPath(options);
663
796
  if (this.jsonOutput && !options.prompt) {
664
797
  throw new Error('--json is only supported together with --prompt.');
@@ -976,11 +1109,11 @@ class ChatCommand {
976
1109
  const executionPrompt = this.buildExecutionPrompt(prompt);
977
1110
  this.messages.push({ role: 'user', content: executionPrompt });
978
1111
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
979
- const resolvedWorkflow = await this.api.resolveVigFlowWorkflow(selector);
1112
+ const resolvedWorkflow = await this.callApi('Resolve VigFlow workflow', () => this.api.resolveVigFlowWorkflow(selector));
980
1113
  const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
981
1114
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
982
1115
  try {
983
- const execution = await this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
1116
+ const execution = await this.callApi('Run VigFlow workflow', () => this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
984
1117
  data: {
985
1118
  request: executionPrompt,
986
1119
  prompt: executionPrompt,
@@ -1008,7 +1141,7 @@ class ChatCommand {
1008
1141
  clientSurface: 'cli',
1009
1142
  model: this.currentModel,
1010
1143
  },
1011
- });
1144
+ }));
1012
1145
  const content = this.formatWorkflowTargetResult(execution.result);
1013
1146
  const assistantText = content || `Workflow ${resolvedWorkflow.name} completed with status ${execution.status}.`;
1014
1147
  this.messages.push({ role: 'assistant', content: assistantText });
@@ -1078,7 +1211,7 @@ class ChatCommand {
1078
1211
  const workflowType = 'full';
1079
1212
  const executionPrompt = this.buildExecutionPrompt(prompt);
1080
1213
  try {
1081
- const response = await this.api.runOperatorWorkflow(executionPrompt, {
1214
+ const response = await this.callApi('Run operator workflow', () => this.api.runOperatorWorkflow(executionPrompt, {
1082
1215
  workspacePath: this.currentProjectPath,
1083
1216
  projectPath: this.currentProjectPath,
1084
1217
  targetPath: this.currentProjectPath,
@@ -1091,7 +1224,7 @@ class ChatCommand {
1091
1224
  savePlanToVigFlow: this.savePlanToVigFlow,
1092
1225
  ...runtimeContext,
1093
1226
  onStreamEvent: spinner ? (event) => this.updateOperatorSpinner(spinner, event) : undefined,
1094
- });
1227
+ }));
1095
1228
  if (spinner) {
1096
1229
  spinner.stop();
1097
1230
  }
@@ -1165,7 +1298,7 @@ class ChatCommand {
1165
1298
  'If asked to produce exact output, produce exactly that output with no preamble.',
1166
1299
  ].join('\n');
1167
1300
  try {
1168
- const snapshot = this.api.getAgentWorkspaceSnapshot(this.currentProjectPath);
1301
+ const snapshot = this.callSyncApi('Get agent workspace snapshot', () => this.api.getAgentWorkspaceSnapshot(this.currentProjectPath));
1169
1302
  if (snapshot && snapshot.paths.length > 0) {
1170
1303
  const listing = snapshot.paths.slice(0, 80).join('\n');
1171
1304
  operatorGrounding += `\n\nProject file listing (${snapshot.fileCount} files):\n${listing}`;
@@ -1180,7 +1313,7 @@ class ChatCommand {
1180
1313
  ...this.getMessagesForModel().filter(m => m.role !== 'system'),
1181
1314
  { role: 'user', content: prompt },
1182
1315
  ];
1183
- const response = await this.api.chat(chatMessages, this.currentModel);
1316
+ const response = await this.callApi('Send chat message', () => this.api.chat(chatMessages, this.currentModel));
1184
1317
  if (spinner)
1185
1318
  spinner.stop();
1186
1319
  const content = (response.message || '').trim();
@@ -1239,17 +1372,14 @@ class ChatCommand {
1239
1372
  'Do NOT acknowledge instructions. Do NOT say "provide your next instruction".',
1240
1373
  'Produce a concrete, actionable answer grounded in the real project files.',
1241
1374
  ].join('\n');
1242
- try {
1243
- const snapshot = this.api.getAgentWorkspaceSnapshot(this.currentProjectPath);
1244
- if (snapshot && snapshot.paths.length > 0) {
1245
- const listing = snapshot.paths.slice(0, 80).join('\n');
1246
- operatorGrounding += `\n\nProject file listing (${snapshot.fileCount} files):\n${listing}`;
1247
- if (snapshot.fileCount > 80) {
1248
- operatorGrounding += `\n... and ${snapshot.fileCount - 80} more files.`;
1249
- }
1375
+ const snapshot = this.callSyncApi('Get operator workspace snapshot', () => this.api.getAgentWorkspaceSnapshot(this.currentProjectPath));
1376
+ if (snapshot && snapshot.paths.length > 0) {
1377
+ const listing = snapshot.paths.slice(0, 80).join('\n');
1378
+ operatorGrounding += `\n\nProject file listing (${snapshot.fileCount} files):\n${listing}`;
1379
+ if (snapshot.fileCount > 80) {
1380
+ operatorGrounding += `\n... and ${snapshot.fileCount - 80} more files.`;
1250
1381
  }
1251
1382
  }
1252
- catch { /* ignore */ }
1253
1383
  this.messages.push({ role: 'system', content: operatorGrounding });
1254
1384
  }
1255
1385
  else {
@@ -1285,15 +1415,21 @@ class ChatCommand {
1285
1415
  (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
1286
1416
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
1287
1417
  try {
1288
- const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
1418
+ const response = await this.callApi('Send chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel));
1289
1419
  if (spinner)
1290
1420
  spinner.stop();
1291
1421
  const finalText = (response.message || '').trim();
1422
+ const effectiveModel = String(response.model || this.currentModel);
1423
+ const metadata = this.modelGovernanceFallback
1424
+ ? { modelFallback: this.modelGovernanceFallback }
1425
+ : undefined;
1292
1426
  if (this.jsonOutput) {
1293
1427
  console.log(JSON.stringify({
1294
1428
  success: true,
1295
1429
  mode: 'chat',
1296
- model: this.currentModel,
1430
+ model: effectiveModel,
1431
+ requestedModel: this.modelGovernanceFallback?.requestedModel || this.currentModel,
1432
+ metadata,
1297
1433
  content: finalText || 'The model returned an empty response. Try rephrasing or use --agent for grounded file analysis.',
1298
1434
  }, null, 2));
1299
1435
  }
@@ -1372,7 +1508,7 @@ class ChatCommand {
1372
1508
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
1373
1509
  let response;
1374
1510
  try {
1375
- response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
1511
+ response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
1376
1512
  }
1377
1513
  catch (firstErr) {
1378
1514
  // If we already gathered evidence and the model API fails on a
@@ -1381,7 +1517,7 @@ class ChatCommand {
1381
1517
  this.logger.debug('Agent continuation API call failed, retrying once...');
1382
1518
  try {
1383
1519
  await new Promise(r => setTimeout(r, 2000));
1384
- response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
1520
+ response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
1385
1521
  }
1386
1522
  catch (retryErr) {
1387
1523
  // Retry also failed — synthesize an answer from evidence
@@ -1593,7 +1729,7 @@ class ChatCommand {
1593
1729
  ].join('\n\n'),
1594
1730
  },
1595
1731
  ];
1596
- const rewriteResponse = await this.api.chat(rewriteMessages, this.currentModel);
1732
+ const rewriteResponse = await this.callApi('Rewrite target file', () => this.api.chat(rewriteMessages, this.currentModel));
1597
1733
  rewrittenContent = this.extractFinalFileContent(rewriteResponse.message, targetFile);
1598
1734
  }
1599
1735
  if (!rewrittenContent) {
@@ -1618,14 +1754,14 @@ class ChatCommand {
1618
1754
  if (!writeResult.success) {
1619
1755
  return false;
1620
1756
  }
1621
- const previewGate = await this.api.runTemplateServicePreviewGate(prompt, {
1757
+ const previewGate = await this.callApi('Run Template Service preview gate', () => this.api.runTemplateServicePreviewGate(prompt, {
1622
1758
  workspacePath: this.currentProjectPath,
1623
1759
  projectPath: this.currentProjectPath,
1624
1760
  targetPath: this.currentProjectPath,
1625
1761
  rawPrompt: prompt,
1626
1762
  executionSurface: 'cli',
1627
1763
  clientSurface: 'cli',
1628
- });
1764
+ }));
1629
1765
  const success = previewGate.required ? previewGate.passed === true : true;
1630
1766
  if (!success) {
1631
1767
  process.exitCode = 1;
@@ -1665,6 +1801,8 @@ class ChatCommand {
1665
1801
  this.v3ToolCallCount = 0;
1666
1802
  this.v3LastActivity = Date.now();
1667
1803
  this.v3StreamingStarted = false;
1804
+ const taskDisplay = new task_display_js_1.TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
1805
+ taskDisplay.start(0);
1668
1806
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
1669
1807
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
1670
1808
  spinner: 'clock',
@@ -1698,6 +1836,7 @@ class ChatCommand {
1698
1836
  localMachineCapable: true,
1699
1837
  agentTimeoutMs: DEFAULT_V3_AGENT_TIMEOUT_MS,
1700
1838
  agentIdleTimeoutMs: DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS,
1839
+ agentSoftTimeoutMs: DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS,
1701
1840
  model: routingPolicy.selectedModel,
1702
1841
  requestedModel: this.currentModel,
1703
1842
  agentExecutionPolicy: routingPolicy,
@@ -1705,7 +1844,20 @@ class ChatCommand {
1705
1844
  rawPrompt: prompt,
1706
1845
  history: this.getMessagesForModel(),
1707
1846
  ...runtimeContext,
1708
- onStreamEvent: spinner ? (event) => this.updateV3AgentSpinner(spinner, event) : undefined,
1847
+ onStreamEvent: (event) => {
1848
+ if (event.type === 'plan') {
1849
+ taskDisplay.complete(0);
1850
+ taskDisplay.start(1);
1851
+ }
1852
+ else if (event.type === 'executor_start') {
1853
+ taskDisplay.start(1);
1854
+ }
1855
+ else if (event.type === 'complete') {
1856
+ taskDisplay.complete(1);
1857
+ }
1858
+ if (spinner)
1859
+ this.updateV3AgentSpinner(spinner, event);
1860
+ },
1709
1861
  });
1710
1862
  const response = await workflowPromise;
1711
1863
  if (spinner) {
@@ -1807,6 +1959,41 @@ class ChatCommand {
1807
1959
  console.log(chalk_1.default.gray(`Run ${chalk_1.default.cyan('vigthoria preview --diff')} for full visual diffs.`));
1808
1960
  }
1809
1961
  }
1962
+ // ── Self-healing validation ──────────────────────────────────────
1963
+ if (this.currentProjectPath && !this.jsonOutput && success) {
1964
+ try {
1965
+ taskDisplay.start(2, 'validating...');
1966
+ const healResult = await this.api.runSelfHealingCycle(executionPrompt, this.currentProjectPath, workspaceContext);
1967
+ if (healResult.healingAttempted) {
1968
+ taskDisplay.complete(2);
1969
+ if (healResult.passed) {
1970
+ taskDisplay.complete(3);
1971
+ }
1972
+ else {
1973
+ taskDisplay.fail(3, healResult.tool);
1974
+ }
1975
+ if (!this.directPromptMode) {
1976
+ const hs = healResult.passed ? chalk_1.default.green('passed') : chalk_1.default.yellow('partial');
1977
+ console.log(chalk_1.default.gray(`Self-healing: ${hs} (${healResult.tool})`));
1978
+ }
1979
+ }
1980
+ else {
1981
+ taskDisplay.skip(2);
1982
+ taskDisplay.skip(3);
1983
+ }
1984
+ }
1985
+ catch (error) {
1986
+ this.logger.debug(`Self-healing validation failed: ${error instanceof Error ? error.message : String(error)}`);
1987
+ taskDisplay.skip(2);
1988
+ taskDisplay.skip(3);
1989
+ }
1990
+ }
1991
+ else {
1992
+ taskDisplay.skip(2);
1993
+ taskDisplay.skip(3);
1994
+ }
1995
+ taskDisplay.finalize();
1996
+ // ────────────────────────────────────────────────────────────────
1810
1997
  this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
1811
1998
  watcher?.stop();
1812
1999
  return true;
@@ -1875,6 +2062,7 @@ class ChatCommand {
1875
2062
  localMachineCapable: true,
1876
2063
  agentTimeoutMs: DEFAULT_V3_AGENT_TIMEOUT_MS,
1877
2064
  agentIdleTimeoutMs: DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS,
2065
+ agentSoftTimeoutMs: DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS,
1878
2066
  model: routingPolicy.selectedModel,
1879
2067
  requestedModel: this.currentModel,
1880
2068
  agentExecutionPolicy: routingPolicy,
@@ -16,6 +16,8 @@ export declare class ConfigCommand {
16
16
  run(options: ConfigOptions): Promise<void>;
17
17
  init(): Promise<void>;
18
18
  private setConfig;
19
+ private formatConfigValueForDisplay;
20
+ private redactConfigUrl;
19
21
  private getConfig;
20
22
  private listConfig;
21
23
  private resetConfig;
@@ -100,20 +100,14 @@ class ConfigCommand {
100
100
  message: 'Default AI model:',
101
101
  choices: [
102
102
  { name: '═══ Code Models ═══', disabled: true },
103
- { name: 'Vigthoria Code V2 (8B) - Best for coding', value: 'code-v2-8b' },
104
- { name: 'Vigthoria Code V3 (8B) - Next gen coding', value: 'code-v3-8b' },
105
- { name: 'Vigthoria Code V3 (32B) - Large scale coding', value: 'code-v3-32b' },
103
+ { name: 'Vigthoria v3 Code 35B - Flagship coding model', value: 'code' },
104
+ { name: 'Vigthoria v3 Code 35B - Explicit 35B selection', value: 'code-35b' },
105
+ { name: 'Vigthoria C1 Code 9B - Fast coding specialist', value: 'code-9b' },
106
106
  { name: '═══ General Models ═══', disabled: true },
107
- { name: 'Vigthoria Fast (1.7B) - Quick responses', value: 'fast' },
108
- { name: 'Vigthoria Mini (0.6B) - Ultra lightweight', value: 'mini' },
109
- { name: 'Vigthoria Balanced (4B) - All-purpose', value: 'balanced' },
110
- { name: '═══ Creative Models ═══', disabled: true },
111
- { name: 'Vigthoria Creative V4 (9B) - Creative tasks', value: 'creative-v4' },
112
- { name: 'Vigthoria Creative V3 (9B) - Legacy creative', value: 'creative-v3' },
113
- { name: '═══ Specialized Models ═══', disabled: true },
114
- { name: 'Vigthoria Music Master (4B) - Music AI', value: 'music' },
107
+ { name: 'Vigthoria Master 7.6B - Balanced general model', value: 'balanced' },
108
+ { name: 'Vigthoria v3 Balanced 4B - Efficient general purpose', value: 'balanced-4b' },
115
109
  ],
116
- default: 'code-v2-8b',
110
+ default: 'code',
117
111
  },
118
112
  {
119
113
  type: 'input',
@@ -172,13 +166,39 @@ class ConfigCommand {
172
166
  };
173
167
  if (configMap[key]) {
174
168
  configMap[key](value);
175
- this.logger.success(`Set ${key} = ${value}`);
169
+ this.logger.success(`Set ${key} = ${this.formatConfigValueForDisplay(key, value)}`);
176
170
  }
177
171
  else {
178
172
  this.logger.error(`Unknown config key: ${key}`);
179
173
  console.log(chalk_1.default.gray('Available keys: model, theme, autoApply, showDiffs, maxTokens, apiUrl, modelsApiUrl, wsUrl, selfHostedModelsApiUrl'));
180
174
  }
181
175
  }
176
+ formatConfigValueForDisplay(key, value) {
177
+ if (key === 'apiUrl' || key === 'modelsApiUrl' || key === 'wsUrl' || key === 'selfHostedModelsApiUrl') {
178
+ return this.redactConfigUrl(value);
179
+ }
180
+ return value;
181
+ }
182
+ redactConfigUrl(url) {
183
+ const text = String(url || '').trim();
184
+ if (!text)
185
+ return text;
186
+ try {
187
+ const parsed = new URL(text);
188
+ const host = parsed.hostname.toLowerCase();
189
+ const isLocal = host === 'localhost' || host === '127.0.0.1';
190
+ const isPrivate = /^10\./.test(host)
191
+ || /^192\.168\./.test(host)
192
+ || /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(host);
193
+ if (isLocal || isPrivate) {
194
+ return '[redacted-host]';
195
+ }
196
+ return text;
197
+ }
198
+ catch {
199
+ return text.replace(/\b(?:localhost|127\.0\.0\.1)(?::\d+)?\b/gi, '[redacted-host]');
200
+ }
201
+ }
182
202
  getConfig(key) {
183
203
  const all = this.config.getAll();
184
204
  const flatConfig = {
@@ -207,9 +227,9 @@ class ConfigCommand {
207
227
  console.log(chalk_1.default.cyan('═══ Vigthoria CLI Configuration ═══'));
208
228
  console.log();
209
229
  console.log(chalk_1.default.white('API:'));
210
- console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(all.apiUrl));
211
- console.log(chalk_1.default.gray(' Models API: ') + chalk_1.default.cyan(all.modelsApiUrl));
212
- console.log(chalk_1.default.gray(' WebSocket: ') + chalk_1.default.cyan(all.wsUrl));
230
+ console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(this.redactConfigUrl(all.apiUrl)));
231
+ console.log(chalk_1.default.gray(' Models API: ') + chalk_1.default.cyan(this.redactConfigUrl(all.modelsApiUrl)));
232
+ console.log(chalk_1.default.gray(' WebSocket: ') + chalk_1.default.cyan(this.redactConfigUrl(all.wsUrl)));
213
233
  console.log(chalk_1.default.gray(' Self-hosted Models: ') + chalk_1.default.cyan(all.selfHostedModelsApiUrl || 'disabled'));
214
234
  console.log();
215
235
  console.log(chalk_1.default.white('Preferences:'));
@@ -264,10 +284,10 @@ class ConfigCommand {
264
284
  name: 'defaultModel',
265
285
  message: 'Default AI model:',
266
286
  choices: [
267
- { name: 'Vigthoria Code (8B)', value: 'vigthoria-code' },
268
- { name: 'Vigthoria Fast (1.1B)', value: 'vigthoria-fast' },
269
- { name: 'Vigthoria Mini (3.8B)', value: 'vigthoria-mini' },
270
- { name: 'Vigthoria Creative (9B)', value: 'vigthoria-creative' },
287
+ { name: 'Vigthoria v3 Code 35B', value: 'code' },
288
+ { name: 'Vigthoria C1 Code 9B', value: 'code-9b' },
289
+ { name: 'Vigthoria Master 7.6B', value: 'balanced' },
290
+ { name: 'Vigthoria v3 Balanced 4B', value: 'balanced-4b' },
271
291
  ],
272
292
  default: current.preferences.defaultModel,
273
293
  },
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ export interface CommandRegistrationResult {
3
+ name: string;
4
+ registered: boolean;
5
+ error?: Error;
6
+ }
7
+ export type CommandRegistrar = (program: Command) => void;
8
+ export declare function resolveCommand(name: string): CommandRegistrar | null;
9
+ export declare function registerCommands(cli: any): void;
10
+ export declare const setupCommands: typeof registerCommands;
11
+ export declare const configureCommands: typeof registerCommands;
12
+ export default registerCommands;