vigthoria-cli 1.6.14 → 1.6.16

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.
@@ -50,6 +50,9 @@ export declare class ChatCommand {
50
50
  private buildTaskShapingInstructions;
51
51
  private buildExecutionPrompt;
52
52
  private getPromptRuntimeContext;
53
+ private v3IterationCount;
54
+ private v3ToolCallCount;
55
+ private v3LastActivity;
53
56
  private describeV3AgentTool;
54
57
  private updateV3AgentSpinner;
55
58
  private updateOperatorSpinner;
@@ -47,9 +47,9 @@ const api_js_1 = require("../utils/api.js");
47
47
  const tools_js_1 = require("../utils/tools.js");
48
48
  const session_js_1 = require("../utils/session.js");
49
49
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
50
- const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS || '1200000';
50
+ const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS || '3900000';
51
51
  const parsed = Number.parseInt(rawValue, 10);
52
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 1200000;
52
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 3900000;
53
53
  })();
54
54
  class ChatCommand {
55
55
  config;
@@ -227,37 +227,87 @@ class ChatCommand {
227
227
  devtoolsBridgeEndpoint: bridgeStatus.endpoint,
228
228
  };
229
229
  }
230
+ v3IterationCount = 0;
231
+ v3ToolCallCount = 0;
232
+ v3LastActivity = Date.now();
230
233
  describeV3AgentTool(toolName) {
231
234
  const normalized = String(toolName || '').toLowerCase();
232
235
  if (/read|grep|search|list|find|glob/.test(normalized)) {
233
- return 'Planning... gathering project context';
236
+ return 'Reading project files';
234
237
  }
235
238
  if (/write|edit|patch|create|delete|replace|rename/.test(normalized)) {
236
- return 'Applying changes...';
239
+ return 'Writing changes';
237
240
  }
238
241
  if (/test|lint|build|run|exec|terminal/.test(normalized)) {
239
- return 'Validating changes...';
242
+ return 'Running verification';
240
243
  }
241
- return 'Working...';
244
+ if (/hyperloop/.test(normalized)) {
245
+ return 'Hyperloop processing';
246
+ }
247
+ return 'Working';
242
248
  }
243
249
  updateV3AgentSpinner(spinner, event) {
244
250
  if (!event || typeof event !== 'object') {
245
251
  return;
246
252
  }
253
+ this.v3LastActivity = Date.now();
247
254
  if (event.type === 'tool_call') {
248
- spinner.text = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
255
+ this.v3ToolCallCount += 1;
256
+ const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
257
+ const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
258
+ const shortTarget = toolTarget ? ` → ${String(toolTarget).split('/').slice(-2).join('/')}` : '';
259
+ spinner.text = chalk_1.default.cyan(`[${this.v3IterationCount}/${this.v3ToolCallCount}] `) + `${toolDesc}${shortTarget}`;
260
+ return;
261
+ }
262
+ if (event.type === 'tool_result') {
263
+ const success = event.success !== false;
264
+ const toolName = event.name || event.tool || '';
265
+ const indicator = success ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
266
+ spinner.text = `${indicator} ${toolName} complete — next step...`;
267
+ return;
268
+ }
269
+ if (event.type === 'thinking') {
270
+ this.v3IterationCount += 1;
271
+ const iterText = event.content || '';
272
+ const iterMatch = iterText.match(/Iteration (\d+)/i);
273
+ const iterNum = iterMatch ? iterMatch[1] : String(this.v3IterationCount);
274
+ spinner.text = chalk_1.default.cyan(`[Iteration ${iterNum}] `) + 'Analyzing...';
249
275
  return;
250
276
  }
251
277
  if (event.type === 'message') {
252
- spinner.text = 'Writing response...';
278
+ const preview = String(event.content || '').slice(0, 80).replace(/\n/g, ' ');
279
+ spinner.text = chalk_1.default.cyan('[Response] ') + (preview || 'Writing response...');
253
280
  return;
254
281
  }
255
282
  if (event.type === 'complete') {
256
- spinner.text = 'Finishing...';
283
+ const elapsed = event.elapsed || '';
284
+ const iters = event.iterations || this.v3IterationCount;
285
+ const tools = event.tool_calls || this.v3ToolCallCount;
286
+ spinner.text = chalk_1.default.green('✓ ') + `Complete — ${iters} iterations, ${tools} tool calls${elapsed ? `, ${elapsed}` : ''}`;
287
+ return;
288
+ }
289
+ if (event.type === 'plan') {
290
+ const planKind = event.plan?.task_kind || event.task_kind || '';
291
+ spinner.text = chalk_1.default.cyan('[Plan] ') + `Task: ${planKind || 'analyzing'}...`;
292
+ return;
293
+ }
294
+ if (event.type === 'error') {
295
+ if (event.checkpointed) {
296
+ spinner.text = chalk_1.default.yellow('[Checkpoint] ') + 'Budget reached — auto-continuing...';
297
+ }
298
+ else {
299
+ spinner.text = chalk_1.default.red('[Error] ') + (event.message || 'Agent error');
300
+ }
301
+ return;
302
+ }
303
+ if (event.type === 'context') {
304
+ spinner.text = chalk_1.default.cyan('[Context] ') + 'Workspace bound...';
257
305
  return;
258
306
  }
259
- if (event.type === 'plan' || event.type === 'analysis' || event.type === 'thinking') {
260
- spinner.text = 'Planning...';
307
+ if (event.type === 'start') {
308
+ this.v3IterationCount = 0;
309
+ this.v3ToolCallCount = 0;
310
+ spinner.text = chalk_1.default.cyan('[Start] ') + 'Agent initialized...';
261
311
  }
262
312
  }
263
313
  updateOperatorSpinner(spinner, event) {
@@ -856,6 +906,10 @@ class ChatCommand {
856
906
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
857
907
  const routingPolicy = this.resolveAgentExecutionPolicy(prompt);
858
908
  const rescueEligible = this.isSaaSRescuePrompt(prompt);
909
+ // Reset streaming counters for new workflow
910
+ this.v3IterationCount = 0;
911
+ this.v3ToolCallCount = 0;
912
+ this.v3LastActivity = Date.now();
859
913
  const spinner = this.jsonOutput ? null : (0, ora_1.default)({
860
914
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
861
915
  spinner: 'clock',
@@ -188,6 +188,7 @@ export declare class APIClient {
188
188
  private getAccessToken;
189
189
  getV3AgentBaseUrls(preferLocal?: boolean): string[];
190
190
  getV3AgentRunUrl(baseUrl: string): string;
191
+ getV3AgentContinueUrl(baseUrl: string): string;
191
192
  getOperatorBaseUrls(): string[];
192
193
  getOperatorStreamUrl(baseUrl: string): string;
193
194
  getMcpBaseUrls(): string[];
@@ -244,7 +245,7 @@ export declare class APIClient {
244
245
  };
245
246
  waitForAgentWorkspaceSettle(context?: Record<string, any>, options?: Record<string, any>): Promise<void>;
246
247
  extractExpectedWorkspaceFiles(message?: string, context?: Record<string, any>): string[];
247
- captureV3AgentStreamMutation(event: any, streamedFiles: Record<string, string>): void;
248
+ captureV3AgentStreamMutation(event: any, streamedFiles: Record<string, string>, serverRoot?: string | null): void;
248
249
  recoverAgentWorkspaceFiles(context?: Record<string, any>, streamedFiles?: Record<string, string>, expectedFiles?: string[]): void;
249
250
  normalizeAgentWorkspaceRelativePath(rawPath: string, rootPath?: string): string;
250
251
  ensureAgentFrontendPolish(message?: string, context?: Record<string, any>): Promise<void>;
package/dist/utils/api.js CHANGED
@@ -26,9 +26,9 @@ const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
26
26
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 90000;
27
27
  })();
28
28
  const DEFAULT_OPERATOR_TIMEOUT_MS = (() => {
29
- const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS || '1200000';
29
+ const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS || '3900000';
30
30
  const parsed = Number.parseInt(rawValue, 10);
31
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 1200000;
31
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 3900000;
32
32
  })();
33
33
  class APIClient {
34
34
  client;
@@ -276,6 +276,12 @@ class APIClient {
276
276
  }
277
277
  return `${baseUrl}/api/v3-agent/run`;
278
278
  }
279
+ getV3AgentContinueUrl(baseUrl) {
280
+ if (/127\.0\.0\.1:8030|localhost:8030/.test(baseUrl)) {
281
+ return `${baseUrl}/api/agent/continue`;
282
+ }
283
+ return `${baseUrl}/api/v3-agent/continue`;
284
+ }
279
285
  getOperatorBaseUrls() {
280
286
  const configuredModelsApiUrl = String(this.config.get('modelsApiUrl') || 'https://api.vigthoria.io').replace(/\/$/, '');
281
287
  const urls = [
@@ -1684,13 +1690,13 @@ menu {
1684
1690
  addMatches(context.agentPrompt);
1685
1691
  return Array.from(candidates);
1686
1692
  }
1687
- captureV3AgentStreamMutation(event, streamedFiles) {
1693
+ captureV3AgentStreamMutation(event, streamedFiles, serverRoot) {
1688
1694
  if (!event || event.type !== 'tool_call' || !streamedFiles) {
1689
1695
  return;
1690
1696
  }
1691
1697
  const args = event.arguments || {};
1692
1698
  if ((event.name === 'write_file' || event.name === 'edit_file') && typeof args.path === 'string') {
1693
- const filePath = this.normalizeAgentWorkspaceRelativePath(args.path);
1699
+ const filePath = this.normalizeAgentWorkspaceRelativePath(args.path, serverRoot || undefined);
1694
1700
  if (!filePath) {
1695
1701
  return;
1696
1702
  }
@@ -1708,9 +1714,21 @@ menu {
1708
1714
  }
1709
1715
  recoverAgentWorkspaceFiles(context = {}, streamedFiles = {}, expectedFiles = []) {
1710
1716
  const rootPath = this.resolveAgentTargetPath(context);
1711
- if (!rootPath || !fs_1.default.existsSync(rootPath) || Object.keys(streamedFiles).length === 0) {
1717
+ if (!rootPath || Object.keys(streamedFiles).length === 0) {
1712
1718
  return;
1713
1719
  }
1720
+ // Create the local workspace directory if it doesn't exist yet.
1721
+ // This is needed for remote CLI clients where the user specified a
1722
+ // path (e.g., C:\vigthoria\Apps\pacman_rogue) that may not have
1723
+ // been created before the V3 run.
1724
+ if (!fs_1.default.existsSync(rootPath)) {
1725
+ try {
1726
+ fs_1.default.mkdirSync(rootPath, { recursive: true });
1727
+ }
1728
+ catch {
1729
+ return;
1730
+ }
1731
+ }
1714
1732
  const targets = expectedFiles.length > 0 ? expectedFiles : Object.keys(streamedFiles);
1715
1733
  for (const targetPath of targets) {
1716
1734
  const relativePath = this.normalizeAgentWorkspaceRelativePath(targetPath, rootPath);
@@ -2142,6 +2160,7 @@ document.addEventListener('DOMContentLoaded', () => {
2142
2160
  const events = [];
2143
2161
  let final = null;
2144
2162
  let contextId = response.headers.get('x-context-id') || String(context.contextId || '').trim() || null;
2163
+ let serverWorkspaceRoot = null;
2145
2164
  const streamedFiles = {};
2146
2165
  const idleTimeoutMs = context.agentIdleTimeoutMs || DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS;
2147
2166
  while (true) {
@@ -2208,7 +2227,10 @@ document.addEventListener('DOMContentLoaded', () => {
2208
2227
  if (!contextId && typeof event.context_id === 'string' && event.context_id.trim()) {
2209
2228
  contextId = event.context_id.trim();
2210
2229
  }
2211
- this.captureV3AgentStreamMutation(event, streamedFiles);
2230
+ if (!serverWorkspaceRoot && event.type === 'context' && typeof event.workspace_root === 'string' && event.workspace_root.trim()) {
2231
+ serverWorkspaceRoot = event.workspace_root.trim();
2232
+ }
2233
+ this.captureV3AgentStreamMutation(event, streamedFiles, serverWorkspaceRoot);
2212
2234
  if (typeof context.onStreamEvent === 'function') {
2213
2235
  try {
2214
2236
  context.onStreamEvent(event);
@@ -2218,6 +2240,19 @@ document.addEventListener('DOMContentLoaded', () => {
2218
2240
  }
2219
2241
  }
2220
2242
  if (event.type === 'error') {
2243
+ if (event.checkpointed && event.task_id) {
2244
+ // Agent checkpointed — return data so caller can auto-continue
2245
+ return {
2246
+ task_id: event.task_id,
2247
+ context_id: contextId,
2248
+ result: final || event,
2249
+ events,
2250
+ files: streamedFiles,
2251
+ partial: true,
2252
+ checkpointed: true,
2253
+ checkpointed_task_id: event.task_id,
2254
+ };
2255
+ }
2221
2256
  if (this.hasAgentWorkspaceOutput(context)) {
2222
2257
  return {
2223
2258
  task_id: events.find((entry) => entry && entry.task_id)?.task_id || null,
@@ -2241,6 +2276,7 @@ document.addEventListener('DOMContentLoaded', () => {
2241
2276
  result: final,
2242
2277
  events,
2243
2278
  files: streamedFiles,
2279
+ serverWorkspaceRoot: serverWorkspaceRoot || null,
2244
2280
  };
2245
2281
  }
2246
2282
  async runV3AgentWorkflow(message, context = {}) {
@@ -2295,6 +2331,66 @@ document.addEventListener('DOMContentLoaded', () => {
2295
2331
  throw new Error(`V3 agent ${response.status}: ${errorText.slice(0, 200)}`);
2296
2332
  }
2297
2333
  const data = await this.collectV3AgentStream(response, requestExecutionContext);
2334
+ // Auto-continuation: if the agent checkpointed (budget exceeded), continue automatically
2335
+ if (data.checkpointed && data.checkpointed_task_id) {
2336
+ const maxContinuations = 10;
2337
+ let continuationData = data;
2338
+ let continuations = 0;
2339
+ while (continuationData.checkpointed && continuationData.checkpointed_task_id && continuations < maxContinuations) {
2340
+ continuations++;
2341
+ if (typeof requestExecutionContext.onStreamEvent === 'function') {
2342
+ try {
2343
+ requestExecutionContext.onStreamEvent({
2344
+ type: 'message',
2345
+ content: `Auto-continuing task (phase ${continuations + 1})...`,
2346
+ });
2347
+ }
2348
+ catch { /* ignore */ }
2349
+ }
2350
+ const continueBody = {
2351
+ task_id: continuationData.checkpointed_task_id,
2352
+ context: requestBody.context,
2353
+ context_id: requestBody.context_id,
2354
+ mcp_context_id: requestBody.mcp_context_id,
2355
+ stream: true,
2356
+ };
2357
+ const continueController = new AbortController();
2358
+ const continueTimeoutId = setTimeout(() => continueController.abort(), timeoutMs);
2359
+ try {
2360
+ const continueHeaders = await this.getV3AgentHeaders();
2361
+ const continueResponse = await fetch(this.getV3AgentContinueUrl(baseUrl), {
2362
+ method: 'POST',
2363
+ headers: { ...continueHeaders, 'Content-Type': 'application/json' },
2364
+ body: JSON.stringify(continueBody),
2365
+ signal: continueController.signal,
2366
+ });
2367
+ if (!continueResponse.ok) {
2368
+ break; // Fall through to normal completion with partial data
2369
+ }
2370
+ continuationData = await this.collectV3AgentStream(continueResponse, requestExecutionContext);
2371
+ }
2372
+ catch {
2373
+ break; // Fall through to normal completion with partial data
2374
+ }
2375
+ finally {
2376
+ clearTimeout(continueTimeoutId);
2377
+ }
2378
+ }
2379
+ // Use the final continuation data for workspace recovery
2380
+ this.recoverAgentWorkspaceFiles(executionContext, continuationData.files || {}, expectedFiles);
2381
+ await this.waitForAgentWorkspaceSettle(executionContext, { expectedFiles });
2382
+ await this.ensureAgentFrontendPolish(message, executionContext);
2383
+ const previewGate = await this.runTemplateServicePreviewGate(message, executionContext);
2384
+ const finalContextId = continuationData.context_id || data.context_id || response.headers.get('x-context-id') || requestExecutionContext.contextId || null;
2385
+ return {
2386
+ content: this.formatV3AgentResponse(continuationData) || this.formatV3AgentResponse(data),
2387
+ taskId: continuationData.task_id || data.task_id || null,
2388
+ contextId: finalContextId,
2389
+ backendUrl: baseUrl,
2390
+ partial: continuationData.checkpointed === true,
2391
+ metadata: { source: 'v3-agent', mode: 'agent', contextId: finalContextId, continuations, previewGate },
2392
+ };
2393
+ }
2298
2394
  const contextId = data.context_id || response.headers.get('x-context-id') || requestExecutionContext.contextId || null;
2299
2395
  const mcpContextId = response.headers.get('x-mcp-context-id') || requestExecutionContext.mcpContextId || null;
2300
2396
  this.recoverAgentWorkspaceFiles(executionContext, data.files || {}, expectedFiles);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.14",
3
+ "version": "1.6.16",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [