vigthoria-cli 1.13.18 → 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.
@@ -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
- constructor(status, message) {
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 message = typeof body === 'object' && body && 'error' in body
210
- ? String(body.error)
211
- : typeof body === 'object' && body && 'message' in body
212
- ? String(body.message)
213
- : `Request failed with status ${response.status}`;
214
- throw new HttpError(response.status, message);
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
  }
@@ -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[];
@@ -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';
@@ -1838,6 +1839,35 @@ export class ChatCommand {
1838
1839
  originalPrompt: saved.originalPrompt ?? null,
1839
1840
  };
1840
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
+ }
1841
1871
  return;
1842
1872
  }
1843
1873
  }
@@ -3193,12 +3223,33 @@ export class ChatCommand {
3193
3223
  ...(resumeTaskIds.length > 0 ? { remaining_task_ids: resumeTaskIds } : {}),
3194
3224
  }
3195
3225
  : executionHints;
3226
+ const previousExecutionId = this.currentSession?.activeAgentRun?.executionId || null;
3227
+ const executionId = `cli-${Date.now()}-${randomUUID().slice(0, 8)}`;
3196
3228
  const workspaceContext = {
3197
3229
  workspacePath: workspacePath,
3198
3230
  projectPath: workspacePath,
3199
3231
  targetPath: workspacePath,
3232
+ contextId: executionId,
3233
+ executionId,
3200
3234
  ...runtimeContext,
3201
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
+ });
3202
3253
  // Start workspace watcher for bidirectional real-time sync
3203
3254
  let watcher = null;
3204
3255
  if (this.shouldStartWorkspaceWatcher(workspacePath)) {
@@ -3252,6 +3303,7 @@ export class ChatCommand {
3252
3303
  return;
3253
3304
  }
3254
3305
  if (event.type === 'plan') {
3306
+ this.updateAgentExecutionCheckpoint({ status: 'running' });
3255
3307
  taskDisplay.complete(0);
3256
3308
  const tasks = event?.plan?.tasks;
3257
3309
  if (Array.isArray(tasks) && tasks.length > 0) {
@@ -3552,6 +3604,15 @@ export class ChatCommand {
3552
3604
  : null,
3553
3605
  finishedAt: Date.now(),
3554
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
+ });
3555
3616
  if (!this.jsonOutput && !this.directPromptMode) {
3556
3617
  if (this.lastAgentRunOutcome) {
3557
3618
  this.printAgentRunSummary(this.lastAgentRunOutcome, runEvaluation, changedFileCount);
@@ -3653,6 +3714,12 @@ export class ChatCommand {
3653
3714
  workspaceSyncIssue: null,
3654
3715
  finishedAt: Date.now(),
3655
3716
  }, prompt);
3717
+ this.updateAgentExecutionCheckpoint({
3718
+ status: 'failed',
3719
+ failedTaskIds: [...liveOutcome.failedTaskIds],
3720
+ unfinishedTaskIds: [...liveOutcome.unfinishedTaskIds],
3721
+ error: liveOutcome.executorError || safeDetail || 'Connection aborted',
3722
+ });
3656
3723
  if (!this.jsonOutput) {
3657
3724
  process.exitCode = 1;
3658
3725
  }
@@ -5056,6 +5123,24 @@ export class ChatCommand {
5056
5123
  sessionSummary: this.currentSession.memorySummary || '',
5057
5124
  });
5058
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
+ }
5059
5144
  async requestPermission(action) {
5060
5145
  if (this.autoApprove) {
5061
5146
  return true;
@@ -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: 'balanced', autoApplyFixes: false },
74
+ safe: { defaultModel: 'assistant', autoApplyFixes: false },
75
75
  balanced: { defaultModel: 'code', autoApplyFixes: false },
76
- fast: { defaultModel: 'code-9b', autoApplyFixes: true },
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 v3 Code 35B - Flagship coding model', value: 'code' },
102
- { name: 'Vigthoria v3 Code 35B - Explicit 35B selection', value: 'code-35b' },
103
- { name: 'Vigthoria v3 Code 9B - Fast coding specialist', value: 'code-9b' },
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 v3 Code 35B', value: 'code' },
298
- { name: 'Vigthoria v3 Code 9B', value: 'code-9b' },
299
- { name: 'Vigthoria Master 7.6B', value: 'balanced' },
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/index.js CHANGED
@@ -49,6 +49,7 @@ import * as fs from 'fs';
49
49
  import * as path from 'path';
50
50
  import * as os from 'os';
51
51
  import { fileURLToPath } from 'url';
52
+ import { createRequire } from 'module';
52
53
  import { createHash } from 'crypto';
53
54
  import axios from 'axios';
54
55
  import { renderDynamicHelp, selectInteractiveCommand } from './utils/command-menu.js';
@@ -1608,9 +1609,57 @@ Examples:
1608
1609
  : process.platform === 'win32' || process.platform === 'linux'
1609
1610
  ? 'supported-desktop'
1610
1611
  : 'unsupported';
1612
+ const nodeMatch = process.versions.node.match(/^(\d+)\.(\d+)/);
1613
+ const nodeCompatible = Boolean(nodeMatch && (Number(nodeMatch[1]) > 20
1614
+ || (Number(nodeMatch[1]) === 20 && Number(nodeMatch[2]) >= 19)));
1615
+ const runtimeRequire = createRequire(import.meta.url);
1616
+ const requiredRuntimePackages = ['axios', 'commander', 'inquirer', 'ws'];
1617
+ const missingRuntimePackages = requiredRuntimePackages.filter((name) => {
1618
+ try {
1619
+ runtimeRequire.resolve(name);
1620
+ return false;
1621
+ }
1622
+ catch {
1623
+ return true;
1624
+ }
1625
+ });
1626
+ const sessionsDir = path.join(os.homedir(), '.vigthoria', 'sessions');
1627
+ let sessionStorageWritable = false;
1628
+ let latestCheckpoint = null;
1629
+ try {
1630
+ fs.mkdirSync(sessionsDir, { recursive: true, mode: 0o700 });
1631
+ const probePath = path.join(sessionsDir, `.doctor-${process.pid}-${Date.now()}.tmp`);
1632
+ fs.writeFileSync(probePath, '{}', { encoding: 'utf8', mode: 0o600 });
1633
+ fs.unlinkSync(probePath);
1634
+ sessionStorageWritable = true;
1635
+ const latestSessionFile = fs.readdirSync(sessionsDir)
1636
+ .filter((name) => name.endsWith('.json'))
1637
+ .map((name) => ({ name, mtime: fs.statSync(path.join(sessionsDir, name)).mtimeMs }))
1638
+ .sort((left, right) => right.mtime - left.mtime)[0]?.name;
1639
+ if (latestSessionFile) {
1640
+ const persisted = JSON.parse(fs.readFileSync(path.join(sessionsDir, latestSessionFile), 'utf8'));
1641
+ const checkpoint = persisted?.activeAgentRun;
1642
+ if (checkpoint && typeof checkpoint === 'object') {
1643
+ latestCheckpoint = {
1644
+ executionId: checkpoint.executionId || null,
1645
+ status: checkpoint.status || null,
1646
+ updatedAt: checkpoint.updatedAt || null,
1647
+ resumable: ['submitted', 'running', 'failed'].includes(String(checkpoint.status || '')),
1648
+ };
1649
+ }
1650
+ }
1651
+ }
1652
+ catch {
1653
+ sessionStorageWritable = false;
1654
+ }
1655
+ const envToken = String(process.env.VIGTHORIA_AUTH_TOKEN || process.env.VIGTHORIA_TOKEN || '').trim();
1656
+ const configuredToken = envToken || String(config.get('authToken') || '').trim();
1657
+ const tokenSegments = configuredToken ? configuredToken.split('.').length : 0;
1611
1658
  const report = {
1612
1659
  cliVersion: VERSION,
1613
1660
  nodeVersion: process.version,
1661
+ nodeCompatible,
1662
+ runtimeDependencies: missingRuntimePackages.length === 0 ? 'complete' : `missing: ${missingRuntimePackages.join(', ')}`,
1614
1663
  platform: `${process.platform} ${process.arch}`,
1615
1664
  clientOs: resolveClientOsSlug(),
1616
1665
  platformSupport,
@@ -1624,6 +1673,14 @@ Examples:
1624
1673
  apiUrl,
1625
1674
  modelsApiUrl,
1626
1675
  loggedIn: config.isAuthenticated(),
1676
+ authToken: configuredToken
1677
+ ? { present: true, source: envToken ? 'environment' : 'secure-config', structure: tokenSegments === 3 ? 'jwt' : 'opaque' }
1678
+ : { present: false, source: null, structure: null },
1679
+ sessionPersistence: {
1680
+ directory: sessionsDir,
1681
+ writable: sessionStorageWritable,
1682
+ latestCheckpoint,
1683
+ },
1627
1684
  subscriptionPlan: subscription.plan || null,
1628
1685
  subscriptionStatus: subscription.status || null,
1629
1686
  offlineMode: offline,
@@ -1638,20 +1695,52 @@ Examples:
1638
1695
  },
1639
1696
  };
1640
1697
  if (options.checkApi && !offline) {
1698
+ const api = new APIClient(config, logger);
1699
+ if (configuredToken) {
1700
+ try {
1701
+ const tokenCheck = await api.validateToken({ allowNetworkFailOpen: false, enforceTokenShape: true });
1702
+ report.authValidation = tokenCheck.valid ? 'authenticated' : `failed (${tokenCheck.error || 'invalid token'})`;
1703
+ }
1704
+ catch (error) {
1705
+ report.authValidation = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
1706
+ }
1707
+ }
1708
+ else {
1709
+ report.authValidation = 'not checked (login required)';
1710
+ }
1641
1711
  try {
1642
- const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 4000, validateStatus: () => true });
1712
+ const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 12000, validateStatus: () => true });
1643
1713
  report.apiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
1644
1714
  }
1645
1715
  catch (error) {
1646
1716
  report.apiHealth = `unreachable (${error.message})`;
1647
1717
  }
1648
1718
  try {
1649
- const probe = await axios.get(`${modelsApiUrl}/health`, { timeout: 4000, validateStatus: () => true });
1650
- report.modelsApiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
1719
+ const probe = await axios.get(`${modelsApiUrl}/health`, { timeout: 12000, validateStatus: () => true });
1720
+ const body = typeof probe.data === 'string' ? probe.data : JSON.stringify(probe.data || {});
1721
+ report.modelsApiHealth = probe.status >= 200 && probe.status < 400
1722
+ ? 'online'
1723
+ : probe.status === 503 && /pre-release validation|closed/i.test(body)
1724
+ ? 'pre-release-closed (Agent mode uses the Coder gateway)'
1725
+ : `status ${probe.status}`;
1651
1726
  }
1652
1727
  catch (error) {
1653
1728
  report.modelsApiHealth = `unreachable (${error.message})`;
1654
1729
  }
1730
+ if (configuredToken) {
1731
+ try {
1732
+ const capabilities = await api.getCapabilityTruthStatus({ workspacePath: process.cwd() });
1733
+ report.agentInfrastructure = {
1734
+ overall: capabilities.overallOk ? 'online' : 'degraded',
1735
+ v3Agent: capabilities.v3Agent.ok ? 'online' : capabilities.v3Agent.error || 'offline',
1736
+ hyperLoop: capabilities.hyperLoop.ok ? 'online' : capabilities.hyperLoop.error || 'offline',
1737
+ repoMemory: capabilities.repoMemory.ok ? 'online' : capabilities.repoMemory.error || 'offline',
1738
+ };
1739
+ }
1740
+ catch (error) {
1741
+ report.agentInfrastructure = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
1742
+ }
1743
+ }
1655
1744
  }
1656
1745
  if (options.json) {
1657
1746
  console.log(JSON.stringify(report, null, 2));
@@ -1673,7 +1762,9 @@ Examples:
1673
1762
  console.log(chalk.gray('\nTip: pass --check-api to verify API reachability.'));
1674
1763
  }
1675
1764
  }
1676
- process.exitCode = 0;
1765
+ const requiredLocalChecksPassed = nodeCompatible && missingRuntimePackages.length === 0 && sessionStorageWritable;
1766
+ const coderReachable = !options.checkApi || offline || report.apiHealth === 'online';
1767
+ process.exitCode = requiredLocalChecksPassed && coderReachable ? 0 : 1;
1677
1768
  });
1678
1769
  // Config command
1679
1770
  program
@@ -362,8 +362,7 @@ export declare class APIClient {
362
362
  private buildLocalWorkspaceSummary;
363
363
  /**
364
364
  * Build complete workspace hydration separately from model context.
365
- * Remote audits must inspect the same byte-complete assets the user has
366
- * locally; context compaction must never become filesystem compaction.
365
+ * Context compaction must not become filesystem compaction.
367
366
  */
368
367
  private buildOutOfBandWorkspaceFiles;
369
368
  /**
@@ -417,7 +416,6 @@ export declare class APIClient {
417
416
  status: string;
418
417
  files: Record<string, string>;
419
418
  file_count: number;
420
- confirmed_mutations?: string[];
421
419
  local_workspace_path?: string;
422
420
  }>;
423
421
  applyV3BackgroundJobFiles(jobId: string, context?: Record<string, any>): Promise<{
package/dist/utils/api.js CHANGED
@@ -673,9 +673,8 @@ export class APIClient {
673
673
  const normalizedRemote = remoteCandidates
674
674
  .filter(Boolean)
675
675
  .map((url) => String(url).replace(/\/$/, ''));
676
- // An explicit V3 endpoint is an operator routing decision, not a fallback.
677
- // Keep it ahead of the public Coder relay so stale remote user credentials
678
- // cannot abort a healthy service-key-authenticated internal CLI run.
676
+ // An explicit endpoint is an operator routing decision. External installs
677
+ // have none and continue to use the public Coder gateway first.
679
678
  const urls = (preferLocal || localTestMode || explicitV3EndpointConfigured) && localCandidates.length > 0
680
679
  ? [...localCandidates, ...normalizedRemote]
681
680
  : [...normalizedRemote, ...localCandidates];
@@ -898,9 +897,6 @@ export class APIClient {
898
897
  return null;
899
898
  }
900
899
  if (withoutQuery.startsWith('/')) {
901
- // Preview/runtime serve a nested HTML entry from its containing
902
- // directory, so `/styles.css` beside `showcase/index.html` resolves to
903
- // `showcase/styles.css` in the hydrated workspace.
904
900
  return this.normalizeWorkspaceRelativePath(path.posix.normalize(path.posix.join(entryDir, withoutQuery.slice(1))));
905
901
  }
906
902
  return this.normalizeWorkspaceRelativePath(path.posix.normalize(path.posix.join(entryDir, withoutQuery)));
@@ -923,9 +919,8 @@ export class APIClient {
923
919
  const assetPattern = /<(?:link|img|source|video|audio)\b[^>]+(?:href|src)=["']([^"']+)["'][^>]*>/gi;
924
920
  while ((match = assetPattern.exec(String(html || ''))) !== null) {
925
921
  const resolved = normalizeAsset(match[1]);
926
- if (resolved) {
922
+ if (resolved)
927
923
  assets.add(resolved);
928
- }
929
924
  }
930
925
  return {
931
926
  css: Array.from(css),
@@ -1618,18 +1613,10 @@ export class APIClient {
1618
1613
  if (used + entryLen > budgetChars) {
1619
1614
  const normalizedPath = this.normalizeWorkspaceRelativePath(filePath);
1620
1615
  if (mandatory.has(normalizedPath)) {
1621
- // Explicit prompt targets are hydration requirements, not ranking
1622
- // hints. Skipping an HTML entry merely because a larger targeted
1623
- // stylesheet consumed the provisional budget creates false preview
1624
- // failures in the remote V3 workspace. Keep the complete explicit
1625
- // file and let later context-compaction phases shed history/metadata.
1626
1616
  trimmed[filePath] = clipped;
1627
1617
  used += entryLen;
1628
1618
  }
1629
1619
  else if (explicitPriority.has(normalizedPath)) {
1630
- // Linked preview assets are important enough to hydrate, but unlike
1631
- // edit targets they may be compacted. Keep a bounded prefix so the
1632
- // remote static server resolves the asset instead of returning 404.
1633
1620
  const envelope = JSON.stringify(filePath).length + 128;
1634
1621
  const available = Math.max(256, budgetChars - used - envelope);
1635
1622
  const dependencyClip = content.length > available
@@ -1666,10 +1653,6 @@ export class APIClient {
1666
1653
  const localWorkspaceRef = localWorkspaceName ? `vigthoria://local-workspace/${localWorkspaceName}` : null;
1667
1654
  const executionSurface = String(resolvedContext.executionSurface || resolvedContext.clientSurface || 'cli');
1668
1655
  const localMachineCapable = resolvedContext.localMachineCapable !== false;
1669
- // A workspace under a configured server root is already available to the
1670
- // V3 service. Keep tool execution on the server in that case; delegating
1671
- // tools back to the CLI can turn server paths into invalid client paths.
1672
- // An explicit caller override remains authoritative for specialist flows.
1673
1656
  const clientToolExecution = resolvedContext.clientToolExecution === false
1674
1657
  ? false
1675
1658
  : (resolvedContext.clientToolExecution === true
@@ -2739,8 +2722,7 @@ menu {
2739
2722
  }
2740
2723
  }
2741
2724
  catch {
2742
- // Missing/unreadable focused HTML remains visible to the server via
2743
- // the original focus list; dependency expansion is best effort.
2725
+ // Best effort: the explicit target remains in focusFiles.
2744
2726
  }
2745
2727
  }
2746
2728
  const hydrationFocusFiles = [...focusFiles, ...linkedFocusAssets];
@@ -2807,8 +2789,7 @@ menu {
2807
2789
  }
2808
2790
  /**
2809
2791
  * Build complete workspace hydration separately from model context.
2810
- * Remote audits must inspect the same byte-complete assets the user has
2811
- * locally; context compaction must never become filesystem compaction.
2792
+ * Context compaction must not become filesystem compaction.
2812
2793
  */
2813
2794
  buildOutOfBandWorkspaceFiles(context) {
2814
2795
  const resolvedContext = this.ensureExecutionContext(context);
@@ -4475,12 +4456,6 @@ document.addEventListener('DOMContentLoaded', () => {
4475
4456
  await this.ensureV3ServiceKey();
4476
4457
  const executionContext = await this.bindExecutionContext({
4477
4458
  ...context,
4478
- // Background callers historically supplied only workspace metadata.
4479
- // Without the request in the execution context, buildV3AgentContext()
4480
- // classified every detached job from an empty prompt and emitted an
4481
- // `analysis_only` workflow. V3 would then inspect the workspace but was
4482
- // correctly forbidden from writing. Preserve an explicit caller value,
4483
- // otherwise make the submitted job request authoritative for routing.
4484
4459
  rawPrompt: context.rawPrompt || context.prompt || message,
4485
4460
  prompt: context.prompt || context.rawPrompt || message,
4486
4461
  backgroundJob: true,
@@ -4649,13 +4624,11 @@ document.addEventListener('DOMContentLoaded', () => {
4649
4624
  return;
4650
4625
  mutatedPaths.add(normalized);
4651
4626
  };
4652
- for (const filePath of data.confirmed_mutations || []) {
4627
+ for (const filePath of (data.confirmed_mutations || []))
4653
4628
  addMutatedPath(filePath);
4654
- }
4655
- for (const event of Array.isArray(job?.events) ? job.events : []) {
4656
- if (event?.type === 'file_mutation' && event?.action !== 'delete') {
4629
+ for (const event of (Array.isArray(job?.events) ? job.events : [])) {
4630
+ if (event?.type === 'file_mutation' && event?.action !== 'delete')
4657
4631
  addMutatedPath(event.path);
4658
- }
4659
4632
  if (event?.type === 'executor_complete') {
4660
4633
  let summary = event.summary;
4661
4634
  if (typeof summary === 'string') {
@@ -5118,7 +5091,7 @@ document.addEventListener('DOMContentLoaded', () => {
5118
5091
  'vigthoria-cloud-ultra',
5119
5092
  ]);
5120
5093
  if (cloudModels.has(resolvedModel)) {
5121
- return 'vigthoria-v3-code-35b';
5094
+ return 'Vigthoria-v4-Code-27B';
5122
5095
  }
5123
5096
  return null;
5124
5097
  }
@@ -5134,10 +5107,10 @@ document.addEventListener('DOMContentLoaded', () => {
5134
5107
  }
5135
5108
  resolvePermittedModelId(shortName) {
5136
5109
  const normalizedRequested = String(shortName || '').trim().toLowerCase();
5137
- const blockedModels = new Set(['fast', 'mini', 'creative', 'creative-v3', 'creative-v4']);
5110
+ const blockedModels = new Set(['mini', 'creative-v3', 'creative-v4']);
5138
5111
  if (blockedModels.has(normalizedRequested)) {
5139
- this.logger.debug(`Blocked governed model ${shortName}; using fallback vigthoria-v3-code-35b`);
5140
- return 'vigthoria-v3-code-35b';
5112
+ this.logger.debug(`Blocked retired model ${shortName}; using fallback Vigthoria-v4-Code-27B`);
5113
+ return 'Vigthoria-v4-Code-27B';
5141
5114
  }
5142
5115
  const resolvedModel = this.resolveModelId(shortName);
5143
5116
  if (this.isCloudModelId(resolvedModel) && !this.canUseCloudModel()) {
@@ -5163,6 +5136,9 @@ document.addEventListener('DOMContentLoaded', () => {
5163
5136
  isSelfHostedPreferredModel(resolvedModel, requestedModel) {
5164
5137
  const normalizedRequested = String(requestedModel || '').toLowerCase();
5165
5138
  const selfHostedModels = new Set([
5139
+ 'Vigthoria-v4-Creative-27B',
5140
+ 'Vigthoria-v4-Code-27B',
5141
+ 'Vigthoria-v4-Assistant-9B',
5166
5142
  'vigthoria-v3-code-35b',
5167
5143
  'vigthoria-v3-code-35b:latest',
5168
5144
  'vigthoria-v3-code-9b',
@@ -5173,6 +5149,8 @@ document.addEventListener('DOMContentLoaded', () => {
5173
5149
  ]);
5174
5150
  return selfHostedModels.has(resolvedModel)
5175
5151
  || normalizedRequested === 'agent'
5152
+ || normalizedRequested === 'architect'
5153
+ || normalizedRequested === 'assistant'
5176
5154
  || normalizedRequested === 'code'
5177
5155
  || normalizedRequested === 'code-30b'
5178
5156
  || normalizedRequested === 'code-35b'
@@ -5183,9 +5161,9 @@ document.addEventListener('DOMContentLoaded', () => {
5183
5161
  }
5184
5162
  getSelfHostedFallbackModelId(resolvedModel, requestedModel) {
5185
5163
  if (this.isSelfHostedPreferredModel(resolvedModel, requestedModel)) {
5186
- return resolvedModel === 'qwen3-coder:latest' ? 'vigthoria-v3-code-35b' : resolvedModel;
5164
+ return resolvedModel === 'qwen3-coder:latest' ? 'Vigthoria-v4-Code-27B' : resolvedModel;
5187
5165
  }
5188
- return 'vigthoria-v3-code-35b';
5166
+ return 'Vigthoria-v4-Code-27B';
5189
5167
  }
5190
5168
  // Streaming chat
5191
5169
  async *chatStream(messages, model) {
@@ -5268,7 +5246,7 @@ document.addEventListener('DOMContentLoaded', () => {
5268
5246
  // (/v1/chat/completions on api.vigthoria.io) which is the only
5269
5247
  // backend that reliably accepts our auth token.
5270
5248
  async chatComplete(systemPrompt, userPrompt, model, maxTokens) {
5271
- const resolvedModel = model ? this.resolvePermittedModelId(model) : 'vigthoria-v3-code-35b';
5249
+ const resolvedModel = model ? this.resolvePermittedModelId(model) : 'Vigthoria-v4-Code-27B';
5272
5250
  const response = await this.modelRouterClient.post('/v1/chat/completions', {
5273
5251
  model: resolvedModel,
5274
5252
  messages: [
@@ -6118,21 +6096,23 @@ document.addEventListener('DOMContentLoaded', () => {
6118
6096
  // ═══════════════════════════════════════════════════════════════
6119
6097
  // Vigthoria server infrastructure models
6120
6098
  // ═══════════════════════════════════════════════════════════════
6121
- 'fast': 'vigthoria-v3-balanced-4b',
6099
+ 'fast': 'Vigthoria-v4-Assistant-9B',
6122
6100
  'mini': 'vigthoria-mini-0.6b',
6123
- 'balanced': 'vigthoria-v3-balanced-4b',
6124
- 'balanced-4b': 'vigthoria-v3-balanced-4b',
6125
- 'creative': 'vigthoria-creative-9b-v4',
6101
+ 'balanced': 'Vigthoria-v4-Assistant-9B',
6102
+ 'balanced-4b': 'Vigthoria-v4-Assistant-9B',
6103
+ 'assistant': 'Vigthoria-v4-Assistant-9B',
6104
+ 'creative': 'Vigthoria-v4-Creative-27B',
6105
+ 'architect': 'Vigthoria-v4-Creative-27B',
6126
6106
  // Code Models - 35B is the default powerhouse
6127
- 'code': 'vigthoria-v3-code-35b', // Vigthoria server infrastructure 35B model
6128
- 'code-30b': 'vigthoria-v3-code-35b',
6129
- 'code-35b': 'vigthoria-v3-code-35b',
6130
- 'code-8b': 'vigthoria-v3-code-9b',
6131
- 'code-9b': 'vigthoria-v3-code-9b',
6132
- 'pro': 'vigthoria-v3-code-35b',
6133
- 'agent': 'vigthoria-v3-code-35b',
6134
- 'vigthoria-code': 'vigthoria-v3-code-35b',
6135
- 'vigthoria-agent': 'vigthoria-v3-code-35b',
6107
+ 'code': 'Vigthoria-v4-Code-27B',
6108
+ 'code-30b': 'Vigthoria-v4-Code-27B',
6109
+ 'code-35b': 'Vigthoria-v4-Code-27B',
6110
+ 'code-8b': 'Vigthoria-v4-Assistant-9B',
6111
+ 'code-9b': 'Vigthoria-v4-Assistant-9B',
6112
+ 'pro': 'Vigthoria-v4-Code-27B',
6113
+ 'agent': 'Vigthoria-v4-Code-27B',
6114
+ 'vigthoria-code': 'Vigthoria-v4-Code-27B',
6115
+ 'vigthoria-agent': 'Vigthoria-v4-Code-27B',
6136
6116
  // ═══════════════════════════════════════════════════════════════
6137
6117
  // VIGTHORIA CLOUD - current billing catalog aliases
6138
6118
  // ═══════════════════════════════════════════════════════════════
@@ -6150,17 +6130,18 @@ document.addEventListener('DOMContentLoaded', () => {
6150
6130
  'cloud-ultra': 'vigthoria-cloud-maximum',
6151
6131
  };
6152
6132
  // If already a full model ID, return as-is
6153
- if (shortName.includes('vigthoria') || shortName.includes('/') || shortName.includes(':')) {
6154
- if (modelMap[shortName]) {
6155
- return modelMap[shortName];
6133
+ const normalizedShortName = String(shortName || '').toLowerCase();
6134
+ if (normalizedShortName.includes('vigthoria') || shortName.includes('/') || shortName.includes(':')) {
6135
+ if (modelMap[normalizedShortName]) {
6136
+ return modelMap[normalizedShortName];
6156
6137
  }
6157
6138
  return shortName;
6158
6139
  }
6159
- return modelMap[shortName] || 'vigthoria-v3-code-35b';
6140
+ return modelMap[normalizedShortName] || 'Vigthoria-v4-Code-27B';
6160
6141
  }
6161
6142
  async getCoderHealth() {
6162
6143
  try {
6163
- const response = await this.client.get('/api/health', { timeout: 5000 });
6144
+ const response = await this.client.get('/api/health', { timeout: 12000 });
6164
6145
  const ok = response.data?.status === 'ok' || response.data?.healthy === true;
6165
6146
  return {
6166
6147
  name: 'Coder API',
@@ -6335,7 +6316,7 @@ document.addEventListener('DOMContentLoaded', () => {
6335
6316
  const candidates = ['/v1/models', '/models', '/api/models', '/api/tags'];
6336
6317
  for (const endpoint of candidates) {
6337
6318
  try {
6338
- const response = await client.get(endpoint, { timeout: 5000 });
6319
+ const response = await client.get(endpoint, { timeout: 12000 });
6339
6320
  const modelCount = this.extractModelCount(response.data);
6340
6321
  if (modelCount > 0) {
6341
6322
  return { modelCount, endpoint };
@@ -6351,7 +6332,7 @@ document.addEventListener('DOMContentLoaded', () => {
6351
6332
  const modelsApiUrl = this.config.get('modelsApiUrl');
6352
6333
  try {
6353
6334
  const [healthResponse, modelProbe] = await Promise.all([
6354
- this.modelRouterClient.get('/health', { timeout: 5000 }),
6335
+ this.modelRouterClient.get('/health', { timeout: 12000 }),
6355
6336
  this.probeModelList(this.modelRouterClient),
6356
6337
  ]);
6357
6338
  const healthOk = this.isHealthyServicePayload(healthResponse.data);
@@ -6384,7 +6365,7 @@ document.addEventListener('DOMContentLoaded', () => {
6384
6365
  }
6385
6366
  try {
6386
6367
  const [healthResponse, modelProbe] = await Promise.all([
6387
- this.selfHostedModelRouterClient.get('/health', { timeout: 5000 }),
6368
+ this.selfHostedModelRouterClient.get('/health', { timeout: 12000 }),
6388
6369
  this.probeModelList(this.selfHostedModelRouterClient),
6389
6370
  ]);
6390
6371
  const healthOk = this.isHealthyServicePayload(healthResponse.data);
@@ -6422,7 +6403,7 @@ document.addEventListener('DOMContentLoaded', () => {
6422
6403
  for (const endpoint of candidates) {
6423
6404
  try {
6424
6405
  const controller = new AbortController();
6425
- const timer = setTimeout(() => controller.abort(), 3000);
6406
+ const timer = setTimeout(() => controller.abort(), 12000);
6426
6407
  const response = await fetch(endpoint, {
6427
6408
  method: 'GET',
6428
6409
  headers,
@@ -6497,9 +6478,13 @@ document.addEventListener('DOMContentLoaded', () => {
6497
6478
  const endpoint = process.env.VIGTHORIA_HYPERLOOP_URL || `${configuredApiUrl}/api/hyperloop/health`;
6498
6479
  try {
6499
6480
  const token = this.getAccessToken();
6481
+ const hyperLoopServiceKey = process.env.VIGTHORIA_HYPERLOOP_SERVICE_KEY || process.env.HYPERLOOP_SERVICE_KEY || '';
6500
6482
  const response = await fetchWithServiceTimeout(endpoint, {
6501
6483
  method: 'GET',
6502
- headers: token ? { Authorization: `Bearer ${token}` } : undefined,
6484
+ headers: {
6485
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
6486
+ ...(hyperLoopServiceKey ? { 'X-Service-Key': hyperLoopServiceKey } : {}),
6487
+ },
6503
6488
  });
6504
6489
  if (!response.ok) {
6505
6490
  throw new Error(`Hyper Loop health ${response.status}`);
@@ -6527,13 +6512,16 @@ document.addEventListener('DOMContentLoaded', () => {
6527
6512
  const endpoint = process.env.VIGTHORIA_HYPERLOOP_EXECUTE_URL || `${configuredApiUrl}/api/hyperloop/execute`;
6528
6513
  const modulesEndpoint = process.env.VIGTHORIA_HYPERLOOP_MODULES_URL || `${configuredApiUrl}/api/hyperloop/modules`;
6529
6514
  const token = this.getAccessToken();
6515
+ const hyperLoopServiceKey = process.env.VIGTHORIA_HYPERLOOP_SERVICE_KEY || process.env.HYPERLOOP_SERVICE_KEY || '';
6516
+ const hyperLoopHeaders = {
6517
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
6518
+ ...(hyperLoopServiceKey ? { 'X-Service-Key': hyperLoopServiceKey } : {}),
6519
+ };
6530
6520
  const projectPath = this.resolveAgentTargetPath(context);
6531
6521
  try {
6532
6522
  const modulesResponse = await fetchWithServiceTimeout(modulesEndpoint, {
6533
6523
  method: 'GET',
6534
- headers: {
6535
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
6536
- },
6524
+ headers: hyperLoopHeaders,
6537
6525
  });
6538
6526
  if (!modulesResponse.ok) {
6539
6527
  throw new Error(`Repo memory modules ${modulesResponse.status}`);
@@ -6545,10 +6533,7 @@ document.addEventListener('DOMContentLoaded', () => {
6545
6533
  try {
6546
6534
  const probeResponse = await fetchWithServiceTimeout(endpoint, {
6547
6535
  method: 'POST',
6548
- headers: {
6549
- 'Content-Type': 'application/json',
6550
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
6551
- },
6536
+ headers: { 'Content-Type': 'application/json', ...hyperLoopHeaders },
6552
6537
  body: JSON.stringify({
6553
6538
  module: 'repo_context_compactor',
6554
6539
  payload: {
@@ -6565,7 +6550,9 @@ document.addEventListener('DOMContentLoaded', () => {
6565
6550
  });
6566
6551
  if (probeResponse.ok) {
6567
6552
  const probeData = await probeResponse.json();
6568
- compactContextLength = String(probeData?.result?.compact_context || '').length;
6553
+ compactContextLength = String(probeData?.result?.compact_context
6554
+ || probeData?.result?.result?.compact_context
6555
+ || '').length;
6569
6556
  }
6570
6557
  }
6571
6558
  catch {
@@ -6682,12 +6669,12 @@ document.addEventListener('DOMContentLoaded', () => {
6682
6669
  });
6683
6670
  }
6684
6671
  async getCapabilityTruthStatus(context = {}) {
6685
- // Wrap each probe with its own 6 s timeout so they always resolve
6686
- // before the outer 8 s race in auth.ts, producing real error messages
6687
- // (ECONNREFUSED, 404, etc.) instead of the generic "Timed out (8s)".
6672
+ // The public edge can legitimately take 5-10 seconds during cold routing.
6673
+ // Keep a finite ceiling, but do not turn a healthy slow edge into the old
6674
+ // hard 10-second preflight failure seen on external client machines.
6688
6675
  const withTimeout = (p, name) => Promise.race([
6689
6676
  p,
6690
- new Promise(resolve => setTimeout(() => resolve({ name, endpoint: '', ok: false, error: 'Service not reachable (6 s timeout)' }), 6000)),
6677
+ new Promise(resolve => setTimeout(() => resolve({ name, endpoint: '', ok: false, error: 'Service not reachable (15 s timeout)' }), 15000)),
6691
6678
  ]);
6692
6679
  const [v3Agent, hyperLoop, repoMemory, devtoolsBridge] = await Promise.all([
6693
6680
  withTimeout(this.getV3AgentHealth(), 'V3 Agent'),
@@ -195,11 +195,13 @@ export class Config {
195
195
  // Vigthoria server infrastructure operational models
196
196
  // ═══════════════════════════════════════════════════════════════
197
197
  const models = [
198
- { id: 'code', name: 'Vigthoria v3 Code 35B', description: 'Native 35B coding model on Blackwell (V3 pipeline)', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
199
- { id: 'code-35b', name: 'Vigthoria v3 Code 35B', description: 'Same flagship model as code', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
200
- { id: 'code-9b', name: 'Vigthoria v3 Code 9B', description: 'Efficient coding specialist for quick tasks', tier: 'local', backendModel: 'vigthoria-v3-code-9b' },
201
- { id: 'balanced', name: 'Vigthoria Balanced 4B', description: 'Balanced general-purpose local model', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
202
- { id: 'balanced-4b', name: 'Vigthoria Balanced 4B', description: 'Efficient 4B general-purpose model (Qwen3.5-4B based)', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
198
+ { id: 'architect', name: 'Vigthoria v4 Creative 27B', description: 'Architect, planning, deep reasoning, and creative systems work', tier: 'local', backendModel: 'Vigthoria-v4-Creative-27B' },
199
+ { id: 'code', name: 'Vigthoria v4 Code 27B', description: 'Dense production executor for coding, debugging, and repository changes', tier: 'local', backendModel: 'Vigthoria-v4-Code-27B' },
200
+ { id: 'assistant', name: 'Vigthoria v4 Assistant 9B', description: 'Low-latency FIM, completion, voice, and diagnostic model', tier: 'local', backendModel: 'Vigthoria-v4-Assistant-9B' },
201
+ { id: 'code-35b', name: 'Vigthoria v4 Code 27B', description: 'Legacy selector mapped to the V4 executor', tier: 'local', backendModel: 'Vigthoria-v4-Code-27B' },
202
+ { id: 'code-9b', name: 'Vigthoria v4 Assistant 9B', description: 'Legacy selector mapped to the V4 assistant', tier: 'local', backendModel: 'Vigthoria-v4-Assistant-9B' },
203
+ { id: 'balanced', name: 'Vigthoria v4 Assistant 9B', description: 'Legacy selector mapped to the V4 assistant', tier: 'local', backendModel: 'Vigthoria-v4-Assistant-9B' },
204
+ { id: 'balanced-4b', name: 'Vigthoria v4 Assistant 9B', description: 'Legacy selector mapped to the V4 assistant', tier: 'local', backendModel: 'Vigthoria-v4-Assistant-9B' },
203
205
  ];
204
206
  if (this.hasCloudAccess()) {
205
207
  models.push({ id: 'cloud-fast', name: 'Vigthoria Cloud Fast', description: 'Fast cloud responses for lighter work', tier: 'cloud', backendModel: 'vigthoria-cloud-fast' }, { id: 'cloud-balanced', name: 'Vigthoria Cloud Balanced', description: 'Default quality/cost balance for chat and coding', tier: 'cloud', backendModel: 'vigthoria-cloud-balanced' }, { id: 'cloud-code', name: 'Vigthoria Cloud Code', description: 'Economical cloud coding and completion', tier: 'cloud', backendModel: 'vigthoria-cloud-code' }, { id: 'cloud-power', name: 'Vigthoria Cloud Power', description: 'Premium general intelligence for demanding work', tier: 'cloud', backendModel: 'vigthoria-cloud-power' }, { id: 'cloud-maximum', name: 'Vigthoria Cloud Maximum', description: 'Maximum power for complex architecture and reviews', tier: 'cloud', backendModel: 'vigthoria-cloud-maximum' },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Vigthoria Balanced 4B — fast agent path classifier (~0.7–2s on local inference :8016).
2
+ * Vigthoria v4 Assistant 9B — fast agent path classifier through the governed router.
3
3
  * Regex/trivial detection always wins; LLM router refines ambiguous build prompts only.
4
4
  */
5
5
  export type AgentRoutePath = 'template-instant' | 'v3-agent' | 'analysis-only';
@@ -23,7 +23,7 @@ export interface BalancedRouterResult {
23
23
  }
24
24
  export declare function parseBalancedRouterJson(raw: string): BalancedRouterJson | null;
25
25
  /**
26
- * Inference bases for Balanced 4B routing only.
26
+ * Inference bases for the V4 Assistant routing lane only.
27
27
  * Local developer machines must opt in (env) — avoids ~1.5s localhost probe on every Windows turn.
28
28
  */
29
29
  export declare function getInferenceRouterBaseUrls(selfHostedUrl: string | null): string[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Vigthoria Balanced 4B — fast agent path classifier (~0.7–2s on local inference :8016).
2
+ * Vigthoria v4 Assistant 9B — fast agent path classifier through the governed router.
3
3
  * Regex/trivial detection always wins; LLM router refines ambiguous build prompts only.
4
4
  */
5
5
  const ROUTER_SYSTEM = `You are Vigthoria Agent Router. Reply with ONE JSON object only. No markdown, no HTML, no code fences.
@@ -14,8 +14,7 @@ fast_path: "template-instant" or null
14
14
 
15
15
  Example: {"path":"template-instant","task_kind":"web-build","fast_path":"template-instant","confidence":0.95,"reason":"trivial html popup page"}`;
16
16
  const INFERENCE_MODEL_IDS = [
17
- 'vigthoria-balanced-4b',
18
- 'vigthoria-v3-balanced-4b',
17
+ 'Vigthoria-v4-Assistant-9B',
19
18
  ];
20
19
  function normalizePath(value) {
21
20
  const p = String(value || '').trim().toLowerCase();
@@ -56,7 +55,7 @@ export function parseBalancedRouterJson(raw) {
56
55
  return null;
57
56
  }
58
57
  /**
59
- * Inference bases for Balanced 4B routing only.
58
+ * Inference bases for the V4 Assistant routing lane only.
60
59
  * Local developer machines must opt in (env) — avoids ~1.5s localhost probe on every Windows turn.
61
60
  */
62
61
  export function getInferenceRouterBaseUrls(selfHostedUrl) {
@@ -69,7 +68,7 @@ export function getInferenceRouterBaseUrls(selfHostedUrl) {
69
68
  || process.env.VIGTHORIA_AGENT_LLM_ROUTER === '1'
70
69
  || process.platform === 'linux';
71
70
  if (allowLocalhost) {
72
- urls.push('http://127.0.0.1:8016');
71
+ urls.push('http://127.0.0.1:4009');
73
72
  }
74
73
  return [...new Set(urls)];
75
74
  }
@@ -47,6 +47,24 @@ export interface Session {
47
47
  agentMode: boolean;
48
48
  operatorMode?: boolean;
49
49
  lastAgentRunOutcome?: Record<string, unknown> | null;
50
+ activeAgentRun?: AgentExecutionCheckpoint | null;
51
+ }
52
+ export interface AgentExecutionCheckpoint {
53
+ executionId: string;
54
+ parentExecutionId?: string | null;
55
+ contextId: string;
56
+ prompt: string;
57
+ originalPrompt: string;
58
+ workspacePath: string;
59
+ userTier: string | null;
60
+ taskType: string;
61
+ workflowType: string;
62
+ status: 'submitted' | 'running' | 'completed' | 'failed';
63
+ submittedAt: string;
64
+ updatedAt: string;
65
+ failedTaskIds: string[];
66
+ unfinishedTaskIds: string[];
67
+ error?: string | null;
50
68
  }
51
69
  export declare class SessionManager {
52
70
  private sessionsDir;
package/install.ps1 CHANGED
@@ -5,7 +5,7 @@
5
5
  $ErrorActionPreference = "Stop"
6
6
 
7
7
  # Configuration
8
- $CLI_VERSION = "1.13.18"
8
+ $CLI_VERSION = "1.13.20"
9
9
  $INSTALL_DIR = "$env:USERPROFILE\.vigthoria"
10
10
  $NPM_PACKAGE = "vigthoria-cli"
11
11
  $GIT_PACKAGE_URL = "git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
package/install.sh CHANGED
@@ -26,7 +26,7 @@ else
26
26
  fi
27
27
 
28
28
  # Configuration
29
- CLI_VERSION="1.13.18"
29
+ CLI_VERSION="1.13.20"
30
30
  INSTALL_DIR="$HOME/.vigthoria"
31
31
  REPO_URL="https://market.vigthoria.io/vigthoria/vigthoria-cli"
32
32
  GIT_PACKAGE_URL="git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.13.18",
3
+ "version": "1.13.20",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -74,6 +74,7 @@
74
74
  "test:v3-workspace-path": "npm run build && node scripts/test-v3-workspace-path.js",
75
75
  "test:v3-server-tool-execution": "npm run build && node scripts/test-v3-server-tool-execution.js",
76
76
  "test:session:project-match": "npm run build && node scripts/test-session-project-match.mjs",
77
+ "test:external-client-resume": "npm run build && node scripts/test-external-client-resume.mjs",
77
78
  "test:v3-stream-mutation": "npm run build && node scripts/test-v3-stream-mutation.js",
78
79
  "test:context:budget": "npm run build && node scripts/test-context-budget.js",
79
80
  "test:v3-client-tool-quality": "npm run build && node scripts/test-v3-client-tool-quality.mjs",
@@ -88,7 +88,7 @@ python3 - << 'PY'
88
88
  import json
89
89
  j=json.load(open('/tmp/vig-balanced.json'))
90
90
  assert j.get('success') is True
91
- assert j.get('model') in ('vigthoria-balanced-4b', 'vigthoria-v3-balanced-4b'), j
91
+ assert j.get('model') in ('Vigthoria-v4-Assistant-9B', 'vigthoria-balanced-4b', 'vigthoria-v3-balanced-4b'), j
92
92
  print('[pass] balanced-4b')
93
93
  PY
94
94
 
@@ -125,6 +125,7 @@ ids={m.get('id','') for m in json.load(open('/tmp/vig-models.json')).get('data',
125
125
  if ids:
126
126
  has_router = any(
127
127
  m in ids for m in (
128
+ 'Vigthoria-v4-Assistant-9B',
128
129
  'vigthoria-balanced-4b', 'vigthoria-balanced-4b:latest',
129
130
  'vigthoria-v3-balanced-4b', 'vigthoria-v3-balanced-4b:latest',
130
131
  'vigthoria-fast-9b', 'vigthoria-fast-9b:latest',
@@ -132,11 +133,12 @@ if ids:
132
133
  )
133
134
  has_creative = any(
134
135
  m in ids for m in (
136
+ 'Vigthoria-v4-Creative-27B',
135
137
  'vigthoria-creative-9b-v4', 'vigthoria-creative-9b-v4:latest',
136
138
  'vigthoria-fast-9b', 'vigthoria-fast-9b:latest',
137
139
  )
138
140
  )
139
- has_code = any(re.search(r'^vigthoria-v3(?:\.\d+)?-code-35b(?:-|:|$)', i) for i in ids)
141
+ has_code = 'Vigthoria-v4-Code-27B' in ids or any(re.search(r'^vigthoria-v3(?:\.\d+)?-code-35b(?:-|:|$)', i) for i in ids)
140
142
  assert has_router, f'missing router model in inventory: {sorted(ids)}'
141
143
  assert has_creative, f'missing creative/fast model in inventory: {sorted(ids)}'
142
144
  assert has_code, f'missing code model in inventory: {sorted(ids)}'
@@ -163,7 +165,7 @@ cat /tmp/vig-release-urls.txt
163
165
 
164
166
  echo "[12.1] no-agent governance fallback"
165
167
  for attempt in 1 2 3; do
166
- if $CLI chat --no-agent --model creative --prompt "say ok" --json >/tmp/vig-creative.json; then
168
+ if $CLI chat --no-agent --model creative-v4 --prompt "say ok" --json >/tmp/vig-creative.json; then
167
169
  break
168
170
  fi
169
171
  if [[ "$attempt" == "3" ]]; then
@@ -177,7 +179,7 @@ python3 - << 'PY'
177
179
  import json
178
180
  j=json.load(open('/tmp/vig-creative.json'))
179
181
  assert j.get('success') is True
180
- assert j.get('model') == 'vigthoria-v3-code-35b', j
182
+ assert j.get('model') == 'Vigthoria-v4-Code-27B', j
181
183
  md=j.get('metadata') or {}
182
184
  fb=md.get('modelFallback') or {}
183
185
  assert fb.get('reason') == 'governance-blocked-model', j