vigthoria-cli 1.10.36 → 1.10.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent-session-menu.d.ts +19 -0
- package/dist/commands/agent-session-menu.js +155 -0
- package/dist/commands/auth.js +68 -51
- package/dist/commands/bridge.js +19 -12
- package/dist/commands/cancel.js +22 -15
- package/dist/commands/chat.d.ts +0 -22
- package/dist/commands/chat.js +402 -1084
- package/dist/commands/config.js +73 -33
- package/dist/commands/deploy.js +123 -83
- package/dist/commands/device.js +61 -21
- package/dist/commands/edit.js +39 -32
- package/dist/commands/explain.js +25 -18
- package/dist/commands/generate.js +44 -37
- package/dist/commands/hub.js +102 -95
- package/dist/commands/index.js +46 -41
- package/dist/commands/legion.js +186 -146
- package/dist/commands/review.js +36 -29
- package/dist/commands/security.js +12 -5
- package/dist/commands/wallet.js +35 -28
- package/dist/commands/workflow.js +20 -13
- package/dist/utils/brain-hub-client.d.ts +32 -0
- package/dist/utils/brain-hub-client.js +52 -0
- package/dist/utils/bridge-client.js +52 -11
- package/dist/utils/codebase-indexer.d.ts +59 -0
- package/dist/utils/codebase-indexer.js +351 -0
- package/dist/utils/context-ranker.js +21 -15
- package/dist/utils/files.js +42 -5
- package/dist/utils/logger.js +50 -42
- package/dist/utils/persona.js +8 -3
- package/dist/utils/post-write-validator.js +29 -22
- package/dist/utils/project-memory.js +23 -16
- package/dist/utils/task-display.js +20 -13
- package/dist/utils/workspace-brain-service.d.ts +43 -0
- package/dist/utils/workspace-brain-service.js +158 -0
- package/dist/utils/workspace-cache.js +26 -18
- package/dist/utils/workspace-stream.js +63 -21
- package/package.json +3 -6
- package/scripts/release/validate-no-go-gates.sh +1 -1
- package/dist/commands/fork.d.ts +0 -17
- package/dist/commands/fork.js +0 -164
- package/dist/commands/history.d.ts +0 -17
- package/dist/commands/history.js +0 -113
- package/dist/commands/preview.d.ts +0 -55
- package/dist/commands/preview.js +0 -467
- package/dist/commands/replay.d.ts +0 -18
- package/dist/commands/replay.js +0 -156
- package/dist/commands/repo.d.ts +0 -97
- package/dist/commands/repo.js +0 -773
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -201
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -1823
- package/dist/utils/api.d.ts +0 -572
- package/dist/utils/api.js +0 -6548
- package/dist/utils/cli-state.d.ts +0 -54
- package/dist/utils/cli-state.js +0 -185
- package/dist/utils/config.d.ts +0 -85
- package/dist/utils/config.js +0 -267
- package/dist/utils/session.d.ts +0 -118
- package/dist/utils/session.js +0 -423
- package/dist/utils/tools.d.ts +0 -274
- package/dist/utils/tools.js +0 -3502
package/dist/commands/chat.js
CHANGED
|
@@ -1,24 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ChatCommand = void 0;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const readline = __importStar(require("readline"));
|
|
45
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
46
|
+
const api_js_1 = require("../utils/api.js");
|
|
47
|
+
const tools_js_1 = require("../utils/tools.js");
|
|
48
|
+
const session_js_1 = require("../utils/session.js");
|
|
49
|
+
const bridge_client_js_1 = require("../utils/bridge-client.js");
|
|
50
|
+
const workspace_stream_js_1 = require("../utils/workspace-stream.js");
|
|
51
|
+
const task_display_js_1 = require("../utils/task-display.js");
|
|
52
|
+
const project_memory_js_1 = require("../utils/project-memory.js");
|
|
53
|
+
const persona_js_1 = require("../utils/persona.js");
|
|
54
|
+
const agent_session_menu_js_1 = require("./agent-session-menu.js");
|
|
15
55
|
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
16
56
|
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
|
|
17
57
|
if (!rawValue) {
|
|
18
|
-
return
|
|
58
|
+
return 300000;
|
|
19
59
|
}
|
|
20
60
|
const parsed = Number.parseInt(rawValue, 10);
|
|
21
|
-
return Number.isFinite(parsed) && parsed
|
|
61
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
|
|
22
62
|
})();
|
|
23
63
|
const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
|
|
24
64
|
const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
|
|
@@ -36,7 +76,7 @@ const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
|
|
|
36
76
|
const parsed = Number.parseInt(rawValue, 10);
|
|
37
77
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 180000;
|
|
38
78
|
})();
|
|
39
|
-
|
|
79
|
+
class ChatCommand {
|
|
40
80
|
config;
|
|
41
81
|
logger;
|
|
42
82
|
api;
|
|
@@ -112,32 +152,32 @@ export class ChatCommand {
|
|
|
112
152
|
message.includes('aborted');
|
|
113
153
|
}
|
|
114
154
|
toUserFacingApiError(error, context) {
|
|
115
|
-
const classified = classifyError(error);
|
|
155
|
+
const classified = (0, api_js_1.classifyError)(error);
|
|
116
156
|
const status = classified.statusCode || (this.isJwtExpirationError(error) ? 401 : 500);
|
|
117
157
|
if (this.isJwtExpirationError(error) || classified.category === 'auth') {
|
|
118
|
-
return new CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
|
|
158
|
+
return new api_js_1.CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
|
|
119
159
|
}
|
|
120
160
|
// Preserve structured API classification first (auth/model/network/etc.)
|
|
121
161
|
// so upstream responses are not relabeled by message heuristics.
|
|
122
162
|
if (classified.category === 'timeout') {
|
|
123
|
-
return new CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
|
|
163
|
+
return new api_js_1.CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
|
|
124
164
|
}
|
|
125
165
|
if (classified.category === 'network') {
|
|
126
|
-
return new CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
|
|
166
|
+
return new api_js_1.CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
|
|
127
167
|
}
|
|
128
168
|
if (classified.category === 'model_backend') {
|
|
129
|
-
return new CLIError(VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
|
|
169
|
+
return new api_js_1.CLIError(api_js_1.VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
|
|
130
170
|
}
|
|
131
|
-
const message = sanitizeUserFacingErrorText(classified.message || `${context} failed`);
|
|
132
|
-
return new CLIError(message, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
|
|
171
|
+
const message = (0, api_js_1.sanitizeUserFacingErrorText)(classified.message || `${context} failed`);
|
|
172
|
+
return new api_js_1.CLIError(message, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
|
|
133
173
|
}
|
|
134
174
|
handleApiError(error, context) {
|
|
135
175
|
const userFacingError = this.toUserFacingApiError(error, context);
|
|
136
176
|
if (!this.jsonOutput) {
|
|
137
|
-
console.error(
|
|
177
|
+
console.error(chalk_1.default.red(`${context} failed: ${userFacingError.message}`));
|
|
138
178
|
}
|
|
139
179
|
const original = error && typeof error === 'object' ? error : { message: String(error) };
|
|
140
|
-
propagateError({
|
|
180
|
+
(0, api_js_1.propagateError)({
|
|
141
181
|
...original,
|
|
142
182
|
message: userFacingError.message,
|
|
143
183
|
statusCode: userFacingError.statusCode,
|
|
@@ -266,7 +306,7 @@ export class ChatCommand {
|
|
|
266
306
|
explicitModel: true,
|
|
267
307
|
heavyTask,
|
|
268
308
|
cloudEligible,
|
|
269
|
-
cloudSelected:
|
|
309
|
+
cloudSelected: this.currentModel === 'cloud' || this.currentModel === 'cloud-reason' || this.currentModel === 'ultra',
|
|
270
310
|
routeReason: 'explicit-model-selection',
|
|
271
311
|
};
|
|
272
312
|
}
|
|
@@ -299,7 +339,7 @@ export class ChatCommand {
|
|
|
299
339
|
routeReason: 'default-v3-agent',
|
|
300
340
|
};
|
|
301
341
|
}
|
|
302
|
-
getMessagesForModel(
|
|
342
|
+
getMessagesForModel() {
|
|
303
343
|
const messages = [...this.messages];
|
|
304
344
|
const personaOverlay = this.buildActivePersonaOverlay();
|
|
305
345
|
if (personaOverlay && !messages.some((message) => message.role === 'system' && message.content.includes('Optional persona overlay: Wiener Grantler mode.'))) {
|
|
@@ -333,136 +373,15 @@ export class ChatCommand {
|
|
|
333
373
|
messages.splice(insertionIndex, 0, memoryMessage);
|
|
334
374
|
}
|
|
335
375
|
}
|
|
336
|
-
if (options?.compact) {
|
|
337
|
-
const compactLimit = 6000;
|
|
338
|
-
const systemMessages = messages.filter((message) => message.role === 'system').map((message) => ({
|
|
339
|
-
...message,
|
|
340
|
-
content: message.content.length > compactLimit
|
|
341
|
-
? `${message.content.slice(0, compactLimit)}\n...[trimmed for first local agent turn]`
|
|
342
|
-
: message.content,
|
|
343
|
-
}));
|
|
344
|
-
const lastUser = [...messages].reverse().find((message) => message.role === 'user');
|
|
345
|
-
return lastUser ? [...systemMessages.slice(0, 2), lastUser] : systemMessages.slice(0, 2);
|
|
346
|
-
}
|
|
347
376
|
return messages;
|
|
348
377
|
}
|
|
349
|
-
normalizeClientV3ToolPath(rawPath) {
|
|
350
|
-
let normalized = String(rawPath || '.').trim().replace(/\\/g, '/');
|
|
351
|
-
if (!normalized || normalized === '/') {
|
|
352
|
-
return '.';
|
|
353
|
-
}
|
|
354
|
-
normalized = normalized.replace(/^vigthoria:\/\/workspace\/?/i, '');
|
|
355
|
-
normalized = normalized.replace(/^\.\/+/, '');
|
|
356
|
-
normalized = normalized.replace(/^workspace\/?/i, '');
|
|
357
|
-
const workspaceRoot = this.currentProjectPath || process.cwd();
|
|
358
|
-
const workspaceName = path.basename(workspaceRoot);
|
|
359
|
-
if (workspaceName) {
|
|
360
|
-
const workspaceKey = workspaceName.toLowerCase().replace(/[\s_./\\-]+/g, '');
|
|
361
|
-
const parts = normalized.split('/').filter(Boolean);
|
|
362
|
-
if (parts.length > 0) {
|
|
363
|
-
const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
|
|
364
|
-
if (firstKey === workspaceKey) {
|
|
365
|
-
normalized = parts.slice(1).join('/') || '.';
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
normalized = normalized.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
370
|
-
return normalized || '.';
|
|
371
|
-
}
|
|
372
|
-
resolveClientV3ToolPath(rawPath) {
|
|
373
|
-
const root = this.currentProjectPath || process.cwd();
|
|
374
|
-
const normalized = this.normalizeClientV3ToolPath(rawPath);
|
|
375
|
-
const absoluteTarget = path.resolve(root, normalized === '.' ? '' : normalized);
|
|
376
|
-
if (fs.existsSync(absoluteTarget)) {
|
|
377
|
-
return normalized;
|
|
378
|
-
}
|
|
379
|
-
const parts = normalized.split('/').filter(Boolean);
|
|
380
|
-
if (parts.length === 0) {
|
|
381
|
-
return '.';
|
|
382
|
-
}
|
|
383
|
-
try {
|
|
384
|
-
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
385
|
-
const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
|
|
386
|
-
for (const entry of entries) {
|
|
387
|
-
const entryKey = entry.name.toLowerCase().replace(/[\s_./\\-]+/g, '');
|
|
388
|
-
if (entryKey === firstKey || entryKey.includes(firstKey) || firstKey.includes(entryKey)) {
|
|
389
|
-
const rest = parts.slice(1).join('/');
|
|
390
|
-
const candidate = rest ? `${entry.name}/${rest}` : entry.name;
|
|
391
|
-
if (fs.existsSync(path.resolve(root, candidate))) {
|
|
392
|
-
return candidate.replace(/\\/g, '/');
|
|
393
|
-
}
|
|
394
|
-
if (!rest) {
|
|
395
|
-
return entry.name.replace(/\\/g, '/');
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
catch {
|
|
401
|
-
// Ignore unreadable workspace roots during path repair.
|
|
402
|
-
}
|
|
403
|
-
return normalized;
|
|
404
|
-
}
|
|
405
|
-
normalizeClientV3ToolArgs(args) {
|
|
406
|
-
const normalizedArgs = { ...args };
|
|
407
|
-
for (const key of ['path', 'file_path', 'file', 'target']) {
|
|
408
|
-
if (typeof normalizedArgs[key] === 'string' && normalizedArgs[key].trim()) {
|
|
409
|
-
normalizedArgs[key] = this.resolveClientV3ToolPath(normalizedArgs[key]);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
return normalizedArgs;
|
|
413
|
-
}
|
|
414
|
-
async executeClientV3Tool(event) {
|
|
415
|
-
if (!this.tools) {
|
|
416
|
-
return { success: false, output: '', error: 'Local agent tools are not initialized.' };
|
|
417
|
-
}
|
|
418
|
-
const name = String(event.name || '').trim();
|
|
419
|
-
const args = (event.arguments && typeof event.arguments === 'object')
|
|
420
|
-
? event.arguments
|
|
421
|
-
: {};
|
|
422
|
-
let toolName = name;
|
|
423
|
-
let toolArgs = {};
|
|
424
|
-
for (const [key, value] of Object.entries(args)) {
|
|
425
|
-
toolArgs[key] = value == null ? '' : String(value);
|
|
426
|
-
}
|
|
427
|
-
toolArgs = this.normalizeClientV3ToolArgs(toolArgs);
|
|
428
|
-
if (name === 'list_directory') {
|
|
429
|
-
toolName = 'list_dir';
|
|
430
|
-
toolArgs = {
|
|
431
|
-
path: toolArgs.path || '.',
|
|
432
|
-
...(toolArgs.recursive === 'true' || toolArgs.recursive === '1' ? { recursive: 'true' } : {}),
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
else if (name === 'search_files') {
|
|
436
|
-
toolName = 'grep';
|
|
437
|
-
toolArgs = {
|
|
438
|
-
path: toolArgs.path || '.',
|
|
439
|
-
pattern: toolArgs.pattern || toolArgs.query || '',
|
|
440
|
-
...(toolArgs.file_pattern ? { includePattern: toolArgs.file_pattern } : {}),
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
else if (name === 'read_file') {
|
|
444
|
-
toolName = 'read_file';
|
|
445
|
-
}
|
|
446
|
-
else if (name === 'write_file' || name === 'edit_file') {
|
|
447
|
-
toolName = name;
|
|
448
|
-
}
|
|
449
|
-
if (!toolName) {
|
|
450
|
-
return { success: false, output: '', error: 'Missing V3 client tool name.' };
|
|
451
|
-
}
|
|
452
|
-
const result = await this.tools.execute({ tool: toolName, args: toolArgs });
|
|
453
|
-
return {
|
|
454
|
-
success: result.success === true,
|
|
455
|
-
output: String(result.output || result.message || ''),
|
|
456
|
-
error: result.error ? String(result.error) : '',
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
378
|
getActivePersonaMode() {
|
|
460
379
|
if (this.personaOverride)
|
|
461
380
|
return this.personaOverride;
|
|
462
|
-
return normalizePersonaMode(this.config.get('persona')) || 'default';
|
|
381
|
+
return (0, persona_js_1.normalizePersonaMode)(this.config.get('persona')) || 'default';
|
|
463
382
|
}
|
|
464
383
|
buildActivePersonaOverlay() {
|
|
465
|
-
return buildPersonaOverlay(this.getActivePersonaMode(), this.getLastUserPrompt());
|
|
384
|
+
return (0, persona_js_1.buildPersonaOverlay)(this.getActivePersonaMode(), this.getLastUserPrompt());
|
|
466
385
|
}
|
|
467
386
|
isDiagnosticPrompt(prompt) {
|
|
468
387
|
return /(startup|start up|won'?t start|doesn'?t start|crash|crashes|error|errors|failing|fails|issue|issues|bug|bugs|diagnos|debug|runtime|log|logs|exception|traceback|stack trace|yaml|blocking|blocker)/i.test(prompt);
|
|
@@ -472,201 +391,52 @@ export class ChatCommand {
|
|
|
472
391
|
* question — these should use analysis_only workflow, not full_autonomy.
|
|
473
392
|
*/
|
|
474
393
|
isAnalysisLookupPrompt(prompt) {
|
|
475
|
-
if (this.isImplementationPrompt(prompt)) {
|
|
476
|
-
return false;
|
|
477
|
-
}
|
|
478
394
|
return /^(what|which|where|how many|who|find|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|count|read|look at|tell me|locate|search for|does .* exist)/i.test(prompt.trim());
|
|
479
395
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
if (/\bwhat(?:'s|\s+is|\s+are)\s+missing\b/i.test(trimmed)) {
|
|
488
|
-
return true;
|
|
489
|
-
}
|
|
490
|
-
if (/\b(game|html5|pacman|rogue|app|site)\b/i.test(trimmed) && /\b(missing|broken|fix|implement|working|playable)\b/i.test(trimmed)) {
|
|
491
|
-
return true;
|
|
492
|
-
}
|
|
493
|
-
if (/continue the previous agent run/i.test(trimmed) && /\b(implement|fix|missing|blocker|remaining)\b/i.test(trimmed)) {
|
|
494
|
-
return true;
|
|
495
|
-
}
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
getWindowsPromptPathRoots() {
|
|
499
|
-
const roots = new Set();
|
|
500
|
-
const add = (value) => {
|
|
501
|
-
const raw = String(value || '').trim();
|
|
502
|
-
if (!raw)
|
|
503
|
-
return;
|
|
504
|
-
try {
|
|
505
|
-
const resolved = path.resolve(raw);
|
|
506
|
-
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
507
|
-
roots.add(resolved);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
catch {
|
|
511
|
-
// Ignore unreadable discovery roots.
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
const home = os.homedir();
|
|
515
|
-
const cwdRoot = path.parse(process.cwd()).root || path.parse(home).root || '';
|
|
516
|
-
add(process.cwd());
|
|
517
|
-
add(home);
|
|
518
|
-
add(path.join(home, 'Desktop'));
|
|
519
|
-
add(path.join(home, 'Documents'));
|
|
520
|
-
add(process.env.USERPROFILE);
|
|
521
|
-
add(process.env.OneDrive);
|
|
522
|
-
add(process.env.OneDriveCommercial);
|
|
523
|
-
add(process.env.OneDriveConsumer);
|
|
524
|
-
if (process.env.OneDrive) {
|
|
525
|
-
add(path.join(process.env.OneDrive, 'Desktop'));
|
|
526
|
-
add(path.join(process.env.OneDrive, 'Documents'));
|
|
527
|
-
}
|
|
528
|
-
if (cwdRoot) {
|
|
529
|
-
add(cwdRoot);
|
|
530
|
-
add(path.join(cwdRoot, 'vigthoria'));
|
|
531
|
-
add(path.join(cwdRoot, 'Vigthoria'));
|
|
532
|
-
}
|
|
533
|
-
return Array.from(roots);
|
|
534
|
-
}
|
|
535
|
-
findPromptDirectoryByName(rawPath) {
|
|
536
|
-
if (os.platform() !== 'win32')
|
|
537
|
-
return null;
|
|
538
|
-
const normalized = String(rawPath || '').trim().replace(/^\/+/, '').replace(/[\\/]+/g, path.sep);
|
|
539
|
-
if (!normalized)
|
|
540
|
-
return null;
|
|
541
|
-
const wantedParts = normalized.split(/[\\/]+/).filter(Boolean);
|
|
542
|
-
const wantedName = wantedParts[wantedParts.length - 1];
|
|
543
|
-
if (!wantedName)
|
|
544
|
-
return null;
|
|
545
|
-
const maxDepth = 3;
|
|
546
|
-
const maxEntries = 2500;
|
|
547
|
-
const roots = this.getWindowsPromptPathRoots();
|
|
548
|
-
for (const root of roots) {
|
|
549
|
-
let visited = 0;
|
|
550
|
-
const direct = path.join(root, normalized);
|
|
396
|
+
extractExplicitLocalPath(prompt) {
|
|
397
|
+
// Try to extract Windows paths (C:\ D:\ etc.)
|
|
398
|
+
// Match drive letter followed by colon and path
|
|
399
|
+
const windowsMatch = prompt.match(/([A-Za-z]:[\\\/][^\s<>"|?*]*)/);
|
|
400
|
+
if (windowsMatch) {
|
|
401
|
+
const candidatePath = windowsMatch[1].replace(/\\/g, '\\').trim();
|
|
551
402
|
try {
|
|
552
|
-
|
|
553
|
-
|
|
403
|
+
const resolved = require('path').resolve(candidatePath);
|
|
404
|
+
const fs = require('fs');
|
|
405
|
+
if (fs.existsSync(resolved)) {
|
|
406
|
+
return resolved;
|
|
554
407
|
}
|
|
555
408
|
}
|
|
556
409
|
catch {
|
|
557
|
-
//
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const fullPath = path.join(current.dir, entry.name);
|
|
578
|
-
if (entry.name.toLowerCase() === wantedName.toLowerCase()) {
|
|
579
|
-
if (wantedParts.length === 1 || fullPath.toLowerCase().endsWith(normalized.toLowerCase())) {
|
|
580
|
-
return fullPath;
|
|
410
|
+
// Path resolution failed
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Try to extract Unix paths: / followed by any characters until we hit quotes, angle brackets, pipes, or sentence boundaries
|
|
414
|
+
// Supports spaces, special chars, camelCase, etc. (anything a filesystem path might have)
|
|
415
|
+
if (!prompt.includes('http://') && !prompt.includes('https://') && !prompt.includes('ftp://')) {
|
|
416
|
+
// Match / and everything after it until we hit delimiters or common sentence words
|
|
417
|
+
const unixMatch = prompt.match(/(\/(?:[^\s<>"|?*]|\s+(?![^\s<>"|?*]))*)/);
|
|
418
|
+
if (unixMatch) {
|
|
419
|
+
let candidatePath = unixMatch[1].trim();
|
|
420
|
+
// Remove trailing common English words that aren't part of path
|
|
421
|
+
candidatePath = candidatePath.replace(/\s+(and|or|at|in|the|to|for|with|from|by|on)$/i, '').trim();
|
|
422
|
+
// Remove trailing punctuation
|
|
423
|
+
candidatePath = candidatePath.replace(/[.,;!?:)\]]*$/, '').trim();
|
|
424
|
+
if (candidatePath.length > 1) {
|
|
425
|
+
try {
|
|
426
|
+
const resolved = require('path').resolve(candidatePath);
|
|
427
|
+
const fs = require('fs');
|
|
428
|
+
if (fs.existsSync(resolved)) {
|
|
429
|
+
return resolved;
|
|
581
430
|
}
|
|
582
431
|
}
|
|
583
|
-
|
|
584
|
-
|
|
432
|
+
catch {
|
|
433
|
+
// Path resolution failed
|
|
585
434
|
}
|
|
586
435
|
}
|
|
587
436
|
}
|
|
588
437
|
}
|
|
589
438
|
return null;
|
|
590
439
|
}
|
|
591
|
-
extractExplicitLocalPath(prompt) {
|
|
592
|
-
const resolveExistingPath = (rawPath) => {
|
|
593
|
-
const trimmed = String(rawPath || '').trim().replace(/^['"]|['"]$/g, '');
|
|
594
|
-
if (!trimmed)
|
|
595
|
-
return null;
|
|
596
|
-
const candidates = [];
|
|
597
|
-
candidates.push(path.resolve(trimmed));
|
|
598
|
-
if (os.platform() === 'win32' && /^\/[A-Za-z0-9._ -]/.test(trimmed)) {
|
|
599
|
-
const withoutLeadingSlash = trimmed.replace(/^\/+/, '');
|
|
600
|
-
const cwdRoot = path.parse(process.cwd()).root || '';
|
|
601
|
-
// Allow prompts like "/Vigthoria Games" to resolve as "C:/Vigthoria/Games".
|
|
602
|
-
const asSegments = withoutLeadingSlash.replace(/\s+/g, path.sep);
|
|
603
|
-
for (const root of this.getWindowsPromptPathRoots()) {
|
|
604
|
-
candidates.push(path.resolve(root, withoutLeadingSlash));
|
|
605
|
-
candidates.push(path.resolve(root, asSegments));
|
|
606
|
-
const segmentParts = asSegments.split(/[\\/]+/).filter(Boolean);
|
|
607
|
-
if (segmentParts.length > 1 && path.basename(root).toLowerCase() === segmentParts[0].toLowerCase()) {
|
|
608
|
-
candidates.push(path.resolve(root, ...segmentParts.slice(1)));
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
if (cwdRoot && !candidates.includes(path.resolve(cwdRoot, withoutLeadingSlash))) {
|
|
612
|
-
candidates.push(path.resolve(cwdRoot, withoutLeadingSlash));
|
|
613
|
-
candidates.push(path.resolve(cwdRoot, asSegments));
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
for (const candidate of candidates) {
|
|
617
|
-
try {
|
|
618
|
-
if (fs.existsSync(candidate)) {
|
|
619
|
-
return candidate;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
catch {
|
|
623
|
-
// Continue trying other normalized candidates.
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
const discovered = this.findPromptDirectoryByName(trimmed);
|
|
627
|
-
if (discovered)
|
|
628
|
-
return discovered;
|
|
629
|
-
return null;
|
|
630
|
-
};
|
|
631
|
-
// Quoted paths first (supports spaces safely).
|
|
632
|
-
const quotedPatterns = [
|
|
633
|
-
/"([A-Za-z]:[\\/][^"\r\n]+)"/,
|
|
634
|
-
/'([A-Za-z]:[\\/][^'\r\n]+)'/,
|
|
635
|
-
/"(\/[^"]+)"/,
|
|
636
|
-
/'(\/[^']+)'/,
|
|
637
|
-
];
|
|
638
|
-
for (const pattern of quotedPatterns) {
|
|
639
|
-
const match = prompt.match(pattern);
|
|
640
|
-
if (match?.[1]) {
|
|
641
|
-
const resolved = resolveExistingPath(match[1]);
|
|
642
|
-
if (resolved)
|
|
643
|
-
return resolved;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
// Unquoted Windows path with optional spaces, stopping before instruction connectors.
|
|
647
|
-
const windowsMatch = prompt.match(/(?:^|\s)([A-Za-z]:[\\/][^<>"|?*\r\n]+?)(?=(?:\s+(?:and|then|to|for|with|where|that|which|who|make|create|build|analyse|analyze)\b|\s{2,}|[.,;!?)]|$))/i);
|
|
648
|
-
if (windowsMatch?.[1]) {
|
|
649
|
-
const resolved = resolveExistingPath(windowsMatch[1]);
|
|
650
|
-
if (resolved)
|
|
651
|
-
return resolved;
|
|
652
|
-
}
|
|
653
|
-
// Unix-style absolute path with optional spaces (but not URLs).
|
|
654
|
-
if (!/(https?|ftp):\/\//i.test(prompt)) {
|
|
655
|
-
const unixMatch = prompt.match(/(?:^|\s)(\/[a-zA-Z0-9._\-/ ]+?)(?=(?:\s+(?:and|then|to|for|with|where|that|which|who|make|create|build|analyse|analyze)\b|\s{2,}|[.,;!?)]|$))/i);
|
|
656
|
-
if (unixMatch?.[1]) {
|
|
657
|
-
const candidatePath = unixMatch[1]
|
|
658
|
-
.replace(/\s+(and|or|at|in|the|to|for|with|from|by|on)$/i, '')
|
|
659
|
-
.replace(/[.,;!?:)\]]*$/, '')
|
|
660
|
-
.trim();
|
|
661
|
-
if (candidatePath.length > 1) {
|
|
662
|
-
const resolved = resolveExistingPath(candidatePath);
|
|
663
|
-
if (resolved)
|
|
664
|
-
return resolved;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
return null;
|
|
669
|
-
}
|
|
670
440
|
isUnscopedPromptPathOverrideAllowed() {
|
|
671
441
|
return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS || ''));
|
|
672
442
|
}
|
|
@@ -713,24 +483,18 @@ export class ChatCommand {
|
|
|
713
483
|
if (this.isUnscopedPromptPathOverrideAllowed()) {
|
|
714
484
|
return candidate;
|
|
715
485
|
}
|
|
716
|
-
const runtime = this.getRuntimeEnvironmentContext();
|
|
717
|
-
// On local-machine CLI sessions, allow explicit absolute paths from user prompts.
|
|
718
|
-
// The path still must exist on disk (validated by extractExplicitLocalPath).
|
|
719
|
-
if (runtime.machineScope === 'local-machine') {
|
|
720
|
-
return candidate;
|
|
721
|
-
}
|
|
722
486
|
const allowedRoots = this.getPromptPathAllowedRoots(baseWorkspace);
|
|
723
487
|
const isAllowed = allowedRoots.some((root) => this.isPathWithinRoot(candidate, root));
|
|
724
488
|
if (isAllowed) {
|
|
725
489
|
return candidate;
|
|
726
490
|
}
|
|
727
491
|
if (!this.jsonOutput) {
|
|
728
|
-
console.log(
|
|
492
|
+
console.log(chalk_1.default.yellow(`Ignoring path outside allowed workspace roots: ${candidate}`));
|
|
729
493
|
if (allowedRoots.length > 0) {
|
|
730
494
|
const displayRoots = allowedRoots.map((root) => root.replace(/\\/g, '/')).join(', ');
|
|
731
|
-
console.log(
|
|
495
|
+
console.log(chalk_1.default.gray(`Allowed roots: ${displayRoots}`));
|
|
732
496
|
}
|
|
733
|
-
console.log(
|
|
497
|
+
console.log(chalk_1.default.gray('To allow unrestricted prompt path overrides, set VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS=1.'));
|
|
734
498
|
}
|
|
735
499
|
return null;
|
|
736
500
|
}
|
|
@@ -781,36 +545,20 @@ export class ChatCommand {
|
|
|
781
545
|
inferAgentTaskType(prompt) {
|
|
782
546
|
if (this.isDiagnosticPrompt(prompt))
|
|
783
547
|
return 'debugging';
|
|
784
|
-
if (this.
|
|
785
|
-
return
|
|
786
|
-
|
|
787
|
-
if (/^(what|which|how many|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|find|count|read|look at|tell me)/i.test(prompt.trim()))
|
|
548
|
+
if (this.isAnalysisLookupPrompt(prompt))
|
|
549
|
+
return 'analysis';
|
|
550
|
+
if (/\b(analy[sz]e|analyse|audit|review|inspect|identify\s+gaps?|gap\s+analysis|production\s+blockers?|read[\s-]?only)\b/i.test(prompt)) {
|
|
788
551
|
return 'analysis';
|
|
789
|
-
return 'implementation';
|
|
790
|
-
}
|
|
791
|
-
bindPromptWorkspace(prompt) {
|
|
792
|
-
const resolved = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
|
|
793
|
-
if (!resolved) {
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
const normalizedResolved = path.resolve(resolved);
|
|
797
|
-
const normalizedCurrent = path.resolve(this.currentProjectPath);
|
|
798
|
-
if (normalizedResolved === normalizedCurrent) {
|
|
799
|
-
return;
|
|
800
552
|
}
|
|
801
|
-
if (
|
|
802
|
-
|
|
553
|
+
if (/\b(implement|build|create|write|fix|repair|scaffold|deploy|add feature)\b/i.test(prompt)
|
|
554
|
+
&& !/\b(analy[sz]e|analyse|audit|review|inspect)\b/i.test(prompt)) {
|
|
555
|
+
return /\b(game|html5|pacman|rogue|playable)\b/i.test(prompt) ? 'game-build' : 'implementation';
|
|
803
556
|
}
|
|
804
|
-
|
|
805
|
-
this.tools?.setWorkspaceRoot(normalizedResolved);
|
|
806
|
-
this.projectMemory = new ProjectMemoryService(normalizedResolved);
|
|
557
|
+
return 'analysis';
|
|
807
558
|
}
|
|
808
559
|
buildTaskShapingInstructions(prompt) {
|
|
809
560
|
const instructions = [];
|
|
810
561
|
const runtime = this.getRuntimeEnvironmentContext();
|
|
811
|
-
if (runtime.machineScope === 'local-machine') {
|
|
812
|
-
instructions.push(`Execution environment: local user machine (${runtime.platform}).`, `Project workspace root on this machine: ${runtime.workspacePath}.`, 'All list_dir, read_file, grep, glob, and bash tools operate on this local filesystem — not a remote server copy.', 'Use paths relative to that root only. Never prefix paths with workspace/ or repeat the workspace folder name.', 'Start with list_dir on "." to discover the real folder structure before reading files.', 'If read_file fails, use list_dir or glob to locate the correct relative path, then retry read_file.');
|
|
813
|
-
}
|
|
814
562
|
// Platform-aware routing hints
|
|
815
563
|
if (runtime.platform === 'windows') {
|
|
816
564
|
instructions.push('Platform: Windows. Use list_dir, glob, read_file, and the grep tool for searching.', 'The grep tool handles Windows automatically — do not use bash to call grep, findstr, or Select-String manually.', 'Do not use bash for Unix commands (cat, head, tail, awk, sed, wc).', 'Use read_file to inspect file contents instead of shell commands.', 'All file paths use forward slashes internally.');
|
|
@@ -821,6 +569,9 @@ export class ChatCommand {
|
|
|
821
569
|
else if (runtime.platform === 'linux') {
|
|
822
570
|
instructions.push('Platform: Linux. Prefer list_dir, grep, and read_file for grounded repository inspection.', 'Use exact file paths from tool output when reporting findings or fixes.');
|
|
823
571
|
}
|
|
572
|
+
if (this.isAnalysisLookupPrompt(prompt)) {
|
|
573
|
+
instructions.push('Read-only analysis mode is active.', 'Inspect the workspace with tools and produce a grounded report of findings, gaps, and blockers.', 'Do not create, modify, or delete files unless the user explicitly asked for changes.');
|
|
574
|
+
}
|
|
824
575
|
if (this.isDiagnosticPrompt(prompt)) {
|
|
825
576
|
instructions.push('Diagnostic mode is active.', 'Treat this as a debugging task, not a generic code review or feature request.', 'Start with concrete evidence: logs, runtime errors, config, launch files, and exact symbol references.', 'If log files exist, inspect them before proposing fixes.', 'Do not claim a file, definition, asset, or symbol is missing until you verify that with tools.', 'If a prior diagnosis mentioned a missing symbol or YAML entry, re-check the actual files before repeating it.', 'Prefer grep plus read_file around the exact references involved in the failure.', 'Separate your reasoning into: Evidence, Confirmed Cause, and Remaining Hypotheses.', 'Do not suggest speculative fixes when the current evidence contradicts them.', 'CRITICAL GROUNDING RULE: Every key name, variable name, symbol, or identifier you mention in your final answer MUST appear verbatim in the tool output you received. If a key/symbol does NOT appear in tool output, you MUST NOT mention it as involved in any conflict or issue.', 'CROSS-FILE ATTRIBUTION: When reporting conflicts between two files, a key/symbol is conflicting ONLY if it appears in BOTH files. Read each file carefully and list only the exact keys that appear in the relevant handler/function of EACH file. Do not assume that because one file handles a key, the other file does too.', 'When reporting conflicts between files, cite the exact file name, line number, and the exact string/key from the tool output. Do not paraphrase or substitute key names.', 'Before concluding, re-check: (1) does every key/symbol in my answer actually appear in the evidence I gathered? (2) for each claimed conflict, did I verify the key appears in BOTH files? If not, correct your answer.');
|
|
826
577
|
}
|
|
@@ -845,7 +596,7 @@ export class ChatCommand {
|
|
|
845
596
|
}
|
|
846
597
|
try {
|
|
847
598
|
if (!this.projectMemory) {
|
|
848
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
599
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
849
600
|
}
|
|
850
601
|
const status = this.projectMemory.getStatus();
|
|
851
602
|
const context = this.projectMemory.buildContextForPrompt(prompt);
|
|
@@ -871,7 +622,7 @@ export class ChatCommand {
|
|
|
871
622
|
}
|
|
872
623
|
try {
|
|
873
624
|
if (!this.projectMemory) {
|
|
874
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
625
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
875
626
|
}
|
|
876
627
|
this.projectMemory.remember(type, text, { source: 'vigthoria-cli', mode, model: this.currentModel });
|
|
877
628
|
}
|
|
@@ -893,7 +644,7 @@ export class ChatCommand {
|
|
|
893
644
|
const devtoolsBridgeAllowed = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_DEVTOOLS_BRIDGE_ALLOWED || process.env.VIGTHORIA_BRIDGE_ALLOWED || ''));
|
|
894
645
|
if (!devtoolsBridgeAllowed) {
|
|
895
646
|
if (!this.jsonOutput) {
|
|
896
|
-
console.log(
|
|
647
|
+
console.log(chalk_1.default.yellow('Browser task detected. DevTools Bridge is opt-in; the agent will ask before relying on local browser tooling.'));
|
|
897
648
|
}
|
|
898
649
|
return {
|
|
899
650
|
...runtimeContext,
|
|
@@ -905,10 +656,10 @@ export class ChatCommand {
|
|
|
905
656
|
}
|
|
906
657
|
const bridgeStatus = await this.callApi('Checking DevTools Bridge status', () => this.api.getDevtoolsBridgeStatus(), 0);
|
|
907
658
|
if (!this.jsonOutput && bridgeStatus.ok) {
|
|
908
|
-
console.log(
|
|
659
|
+
console.log(chalk_1.default.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
|
|
909
660
|
}
|
|
910
661
|
else if (!this.jsonOutput) {
|
|
911
|
-
console.log(
|
|
662
|
+
console.log(chalk_1.default.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
|
|
912
663
|
}
|
|
913
664
|
return {
|
|
914
665
|
...runtimeContext,
|
|
@@ -922,9 +673,6 @@ export class ChatCommand {
|
|
|
922
673
|
v3ToolCallCount = 0;
|
|
923
674
|
v3LastActivity = Date.now();
|
|
924
675
|
v3StreamingStarted = false;
|
|
925
|
-
v3StreamedTextBuffer = '';
|
|
926
|
-
v3LiveToolEvidence = [];
|
|
927
|
-
v3PendingToolCalls = [];
|
|
928
676
|
/**
|
|
929
677
|
* Strip server-internal path prefixes from tool output strings.
|
|
930
678
|
* Prevents exposing paths like /var/www/V3-Code-Agent/... to end users.
|
|
@@ -932,7 +680,7 @@ export class ChatCommand {
|
|
|
932
680
|
sanitizeServerPath(text) {
|
|
933
681
|
if (!text)
|
|
934
682
|
return text;
|
|
935
|
-
return sanitizeUserFacingPathText(this.stripHiddenThoughtBlocks(text));
|
|
683
|
+
return (0, api_js_1.sanitizeUserFacingPathText)(this.stripHiddenThoughtBlocks(text));
|
|
936
684
|
}
|
|
937
685
|
stripHiddenThoughtBlocks(text) {
|
|
938
686
|
if (!text)
|
|
@@ -974,12 +722,12 @@ export class ChatCommand {
|
|
|
974
722
|
const source = (event && typeof event === 'object' && !Buffer.isBuffer(event) && !(event instanceof Uint8Array))
|
|
975
723
|
? (event.body ?? event.stream ?? event.response ?? event)
|
|
976
724
|
: event;
|
|
977
|
-
for await (const chunk of robustifyStreamResponse(source)) {
|
|
725
|
+
for await (const chunk of (0, tools_js_1.robustifyStreamResponse)(source)) {
|
|
978
726
|
this.v3LastActivity = Date.now();
|
|
979
727
|
if (chunk.type === 'error') {
|
|
980
728
|
if (spinner.isSpinning)
|
|
981
729
|
spinner.stop();
|
|
982
|
-
process.stderr.write(
|
|
730
|
+
process.stderr.write(chalk_1.default.red(`\nStream error: ${chunk.content}\n`));
|
|
983
731
|
continue;
|
|
984
732
|
}
|
|
985
733
|
if (chunk.content) {
|
|
@@ -991,7 +739,7 @@ export class ChatCommand {
|
|
|
991
739
|
const message = error instanceof Error ? error.message : String(error);
|
|
992
740
|
if (spinner.isSpinning)
|
|
993
741
|
spinner.stop();
|
|
994
|
-
process.stderr.write(
|
|
742
|
+
process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
|
|
995
743
|
}
|
|
996
744
|
}
|
|
997
745
|
writeV3StreamText(spinner, text) {
|
|
@@ -999,7 +747,6 @@ export class ChatCommand {
|
|
|
999
747
|
if (!safeText) {
|
|
1000
748
|
return;
|
|
1001
749
|
}
|
|
1002
|
-
this.v3StreamedTextBuffer += safeText;
|
|
1003
750
|
if (!this.v3StreamingStarted) {
|
|
1004
751
|
this.v3StreamingStarted = true;
|
|
1005
752
|
spinner.stop();
|
|
@@ -1012,124 +759,13 @@ export class ChatCommand {
|
|
|
1012
759
|
}
|
|
1013
760
|
process.stdout.write(safeText);
|
|
1014
761
|
}
|
|
1015
|
-
isGenericV3AgentContent(text) {
|
|
1016
|
-
const value = String(text || '').trim();
|
|
1017
|
-
return !value || /^(v3 agent workflow completed\.?|task completed|agent run finished|workflow completed\.?)$/i.test(value);
|
|
1018
|
-
}
|
|
1019
|
-
hasAlreadyStreamedV3Content(text) {
|
|
1020
|
-
const value = String(text || '').trim();
|
|
1021
|
-
if (!value)
|
|
1022
|
-
return true;
|
|
1023
|
-
return this.v3StreamedTextBuffer.includes(value) || value.includes(this.v3StreamedTextBuffer.trim());
|
|
1024
|
-
}
|
|
1025
|
-
isThinV3Summary(text) {
|
|
1026
|
-
const value = String(text || '').trim();
|
|
1027
|
-
if (!value)
|
|
1028
|
-
return true;
|
|
1029
|
-
if (/^#\s*Workspace overview/m.test(value) || /^## Workspace analysis \(from local file inspection\)/m.test(value)) {
|
|
1030
|
-
return false;
|
|
1031
|
-
}
|
|
1032
|
-
if (this.isGenericV3AgentContent(value))
|
|
1033
|
-
return true;
|
|
1034
|
-
if (/^The V3 agent finished without emitting a dedicated final answer/i.test(value)) {
|
|
1035
|
-
return !/(## Files read|## Directories inspected|### )/i.test(value);
|
|
1036
|
-
}
|
|
1037
|
-
return value.length < 120;
|
|
1038
|
-
}
|
|
1039
|
-
shouldPrintV3FinalContent(text) {
|
|
1040
|
-
if (!text || this.isGenericV3AgentContent(text)) {
|
|
1041
|
-
return false;
|
|
1042
|
-
}
|
|
1043
|
-
const value = text.trim();
|
|
1044
|
-
if (/^## Workspace analysis \(from local file inspection\)/m.test(value)) {
|
|
1045
|
-
return true;
|
|
1046
|
-
}
|
|
1047
|
-
if (/Reconstructed task summary|Workspace analysis \(from local file inspection\)/i.test(value)) {
|
|
1048
|
-
return true;
|
|
1049
|
-
}
|
|
1050
|
-
if (!this.v3StreamingStarted) {
|
|
1051
|
-
return true;
|
|
1052
|
-
}
|
|
1053
|
-
return !this.hasAlreadyStreamedV3Content(value);
|
|
1054
|
-
}
|
|
1055
|
-
rememberV3ToolEvidence(event, args = {}) {
|
|
1056
|
-
const output = typeof event?.output === 'string' ? event.output.trim() : '';
|
|
1057
|
-
const errorText = typeof event?.error === 'string' ? event.error.trim() : '';
|
|
1058
|
-
const combined = output || errorText;
|
|
1059
|
-
if (!combined) {
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
const target = String(args.path || args.file_path || args.file || args.target || event?.target || '').trim();
|
|
1063
|
-
this.v3LiveToolEvidence.push({
|
|
1064
|
-
name: String(event?.name || event?.tool || 'unknown_tool'),
|
|
1065
|
-
target: target || undefined,
|
|
1066
|
-
arguments: args,
|
|
1067
|
-
output: combined,
|
|
1068
|
-
success: event?.success !== false,
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
buildUserFacingV3RunReport(prompt, workspacePath, options = {}) {
|
|
1072
|
-
const evidenceBody = this.api.formatV3AgentResponse({
|
|
1073
|
-
events: [],
|
|
1074
|
-
liveToolEvidence: this.v3LiveToolEvidence,
|
|
1075
|
-
});
|
|
1076
|
-
const successes = this.v3LiveToolEvidence.filter((entry) => entry.success !== false);
|
|
1077
|
-
const failures = this.v3LiveToolEvidence.filter((entry) => entry.success === false);
|
|
1078
|
-
if (!evidenceBody && successes.length === 0 && failures.length === 0) {
|
|
1079
|
-
return [
|
|
1080
|
-
'# Agent run summary',
|
|
1081
|
-
'',
|
|
1082
|
-
`**Workspace:** ${workspacePath}`,
|
|
1083
|
-
`**Your request:** ${prompt.trim()}`,
|
|
1084
|
-
'',
|
|
1085
|
-
'The agent finished without successfully reading local files, so no grounded overview could be built.',
|
|
1086
|
-
options.serverNote ? `\n**Server note:** ${options.serverNote}` : '',
|
|
1087
|
-
'',
|
|
1088
|
-
'Try `/continue` with: "Read Vigthoria-dominion/package.json, game.js, src/Game.js, and src/factions/, then write a full overview."',
|
|
1089
|
-
].filter(Boolean).join('\n');
|
|
1090
|
-
}
|
|
1091
|
-
const lines = [
|
|
1092
|
-
'# Workspace overview',
|
|
1093
|
-
'',
|
|
1094
|
-
`**Workspace:** ${workspacePath}`,
|
|
1095
|
-
`**Your request:** ${prompt.trim()}`,
|
|
1096
|
-
options.partial
|
|
1097
|
-
? '**Status:** Partial — the agent used its iteration budget before writing a final narrative. The report below is rebuilt from files it actually read on your machine.'
|
|
1098
|
-
: '**Status:** Complete — summary rebuilt from local file inspection.',
|
|
1099
|
-
'',
|
|
1100
|
-
evidenceBody || '_No readable file excerpts were captured._',
|
|
1101
|
-
];
|
|
1102
|
-
if (failures.length > 0) {
|
|
1103
|
-
const uniqueFails = new Map();
|
|
1104
|
-
for (const entry of failures) {
|
|
1105
|
-
const failPath = entry.target || entry.arguments?.path || entry.name;
|
|
1106
|
-
if (!uniqueFails.has(failPath)) {
|
|
1107
|
-
uniqueFails.set(failPath, entry.output.split('\n').find(Boolean)?.slice(0, 140) || entry.output.slice(0, 140));
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
if (uniqueFails.size > 0 && !/## Paths not found/i.test(evidenceBody || '')) {
|
|
1111
|
-
lines.push('', '## Paths not found (exploration misses)', ...[...uniqueFails.entries()].slice(0, 12).map(([failPath, detail]) => `- \`${failPath}\`${detail ? ` — ${detail}` : ''}`), '', '_These are usually incorrect guesses (for example `Vigthoria-dominion/entities` instead of `Vigthoria-dominion/src/entities`). They do not mean your project is broken._');
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
lines.push('', '---', 'Use `/continue` if you want a longer narrative summary or a deep dive into a specific subfolder.');
|
|
1115
|
-
return lines.join('\n');
|
|
1116
|
-
}
|
|
1117
|
-
printV3UserReport(report) {
|
|
1118
|
-
const value = String(report || '').trim();
|
|
1119
|
-
if (!value || this.isGenericV3AgentContent(value)) {
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
console.log();
|
|
1123
|
-
console.log(value);
|
|
1124
|
-
console.log();
|
|
1125
|
-
}
|
|
1126
762
|
updateV3AgentSpinner(spinner, event) {
|
|
1127
763
|
if (this.isRawV3StreamPayload(event)) {
|
|
1128
764
|
this.consumeV3StreamPayload(spinner, event).catch((error) => {
|
|
1129
765
|
const message = error instanceof Error ? error.message : String(error);
|
|
1130
766
|
if (spinner.isSpinning)
|
|
1131
767
|
spinner.stop();
|
|
1132
|
-
process.stderr.write(
|
|
768
|
+
process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
|
|
1133
769
|
});
|
|
1134
770
|
return;
|
|
1135
771
|
}
|
|
@@ -1140,38 +776,23 @@ export class ChatCommand {
|
|
|
1140
776
|
if (event.type === 'tool_call') {
|
|
1141
777
|
this.v3ToolCallCount += 1;
|
|
1142
778
|
const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
|
|
1143
|
-
const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern ||
|
|
779
|
+
const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
|
|
1144
780
|
const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
|
|
1145
781
|
const shortTarget = sanitizedTarget ? ` → ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
|
|
1146
|
-
const stepLabel =
|
|
782
|
+
const stepLabel = chalk_1.default.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
|
|
1147
783
|
if (spinner.isSpinning)
|
|
1148
784
|
spinner.stop();
|
|
1149
785
|
process.stderr.write(stepLabel + '\n');
|
|
1150
786
|
// Show extra detail for key tools
|
|
1151
787
|
const args = event.arguments || {};
|
|
1152
788
|
const toolName = event.name || event.tool || '';
|
|
1153
|
-
this.v3PendingToolCalls.push({
|
|
1154
|
-
name: String(toolName || 'unknown_tool'),
|
|
1155
|
-
args: args && typeof args === 'object' ? args : {},
|
|
1156
|
-
});
|
|
1157
|
-
if (toolName === 'search_files') {
|
|
1158
|
-
const pattern = String(args.pattern || args.query || '').trim();
|
|
1159
|
-
const searchPath = String(args.path || '.').trim();
|
|
1160
|
-
process.stderr.write(chalk.gray(` search: ${pattern || '(empty)'} in ${this.sanitizeServerPath(searchPath)}\n`));
|
|
1161
|
-
}
|
|
1162
|
-
else if (toolName === 'list_directory') {
|
|
1163
|
-
process.stderr.write(chalk.gray(` list: ${this.sanitizeServerPath(String(args.path || '.'))}${args.recursive ? ' (recursive)' : ''}\n`));
|
|
1164
|
-
}
|
|
1165
|
-
else if (toolName === 'read_file') {
|
|
1166
|
-
process.stderr.write(chalk.gray(` read: ${this.sanitizeServerPath(String(args.path || args.file_path || ''))}\n`));
|
|
1167
|
-
}
|
|
1168
789
|
if ((toolName === 'write_file' || toolName === 'edit_file') && typeof args.content === 'string') {
|
|
1169
790
|
const len = args.content.length;
|
|
1170
|
-
process.stderr.write(
|
|
791
|
+
process.stderr.write(chalk_1.default.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
|
|
1171
792
|
}
|
|
1172
793
|
else if (toolName === 'bash' && typeof args.command === 'string') {
|
|
1173
794
|
const command = this.sanitizeServerPath(args.command);
|
|
1174
|
-
process.stderr.write(
|
|
795
|
+
process.stderr.write(chalk_1.default.gray(` $ ${command.slice(0, 120)}${command.length > 120 ? '…' : ''}\n`));
|
|
1175
796
|
}
|
|
1176
797
|
spinner.start();
|
|
1177
798
|
spinner.text = `Running ${toolDesc}...`;
|
|
@@ -1180,28 +801,21 @@ export class ChatCommand {
|
|
|
1180
801
|
if (event.type === 'tool_result') {
|
|
1181
802
|
const success = event.success !== false;
|
|
1182
803
|
const toolName = event.name || event.tool || '';
|
|
1183
|
-
const indicator = success ?
|
|
804
|
+
const indicator = success ? chalk_1.default.green(' ✓') : chalk_1.default.red(' ✗');
|
|
1184
805
|
if (spinner.isSpinning)
|
|
1185
806
|
spinner.stop();
|
|
1186
807
|
process.stderr.write(`${indicator} ${toolName}\n`);
|
|
1187
808
|
// Show output for failures, or brief summary for successes — sanitize server paths
|
|
1188
809
|
const rawOutput = typeof event.output === 'string' ? event.output.trim() : '';
|
|
1189
810
|
const output = this.sanitizeServerPath(rawOutput);
|
|
1190
|
-
const pendingIndex = this.v3PendingToolCalls.findIndex((call) => call.name === toolName);
|
|
1191
|
-
const pendingCall = pendingIndex >= 0
|
|
1192
|
-
? this.v3PendingToolCalls.splice(pendingIndex, 1)[0]
|
|
1193
|
-
: this.v3PendingToolCalls.shift();
|
|
1194
|
-
this.rememberV3ToolEvidence(event, pendingCall?.args || {});
|
|
1195
811
|
if (!success && output) {
|
|
1196
812
|
const sanitizedError = this.sanitizeServerPath(typeof event.error === 'string' ? event.error : output);
|
|
1197
813
|
const lines = sanitizedError.split('\n').slice(0, 4);
|
|
1198
|
-
process.stderr.write(
|
|
814
|
+
process.stderr.write(chalk_1.default.red(` ${lines.join('\n ')}\n`));
|
|
1199
815
|
}
|
|
1200
816
|
else if (success && output && output.length > 0) {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
const brief = firstMeaningfulLine.slice(0, 160);
|
|
1204
|
-
process.stderr.write(chalk.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
|
|
817
|
+
const brief = output.split('\n')[0].slice(0, 120);
|
|
818
|
+
process.stderr.write(chalk_1.default.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
|
|
1205
819
|
}
|
|
1206
820
|
spinner.start();
|
|
1207
821
|
spinner.text = 'Next step...';
|
|
@@ -1211,27 +825,12 @@ export class ChatCommand {
|
|
|
1211
825
|
spinner.text = this.sanitizeServerPath(String(event.content || 'Routing to V3 Agent...'));
|
|
1212
826
|
return;
|
|
1213
827
|
}
|
|
1214
|
-
if (event.type === 'queued') {
|
|
1215
|
-
if (spinner.isSpinning)
|
|
1216
|
-
spinner.stop();
|
|
1217
|
-
process.stderr.write(chalk.yellow(' [Queue] ') + this.sanitizeServerPath(String(event.message || 'Waiting for V3 capacity...')) + '\n');
|
|
1218
|
-
spinner.start();
|
|
1219
|
-
spinner.text = 'Waiting for V3...';
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1222
|
-
if (event.type === 'heartbeat') {
|
|
1223
|
-
spinner.text = 'V3 Agent is still thinking...';
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
828
|
if (event.type === 'thinking') {
|
|
1227
829
|
this.v3IterationCount += 1;
|
|
1228
830
|
const iterText = this.sanitizeServerPath(event.content || '');
|
|
1229
831
|
if (spinner.isSpinning)
|
|
1230
832
|
spinner.stop();
|
|
1231
|
-
process.stderr.write(
|
|
1232
|
-
if (iterText) {
|
|
1233
|
-
process.stderr.write(chalk.gray(`${iterText}\n`));
|
|
1234
|
-
}
|
|
833
|
+
process.stderr.write(chalk_1.default.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
|
|
1235
834
|
spinner.start();
|
|
1236
835
|
spinner.text = 'Analyzing...';
|
|
1237
836
|
return;
|
|
@@ -1245,7 +844,7 @@ export class ChatCommand {
|
|
|
1245
844
|
this.writeV3StreamText(spinner, text);
|
|
1246
845
|
}
|
|
1247
846
|
else {
|
|
1248
|
-
spinner.text =
|
|
847
|
+
spinner.text = chalk_1.default.cyan('[Response] ') + 'Writing response...';
|
|
1249
848
|
}
|
|
1250
849
|
return;
|
|
1251
850
|
}
|
|
@@ -1263,15 +862,15 @@ export class ChatCommand {
|
|
|
1263
862
|
const fail = event.tasks_failed ?? 0;
|
|
1264
863
|
statLine += ` — ${ok} tasks done`;
|
|
1265
864
|
if (fail > 0)
|
|
1266
|
-
statLine +=
|
|
865
|
+
statLine += chalk_1.default.yellow(`, ${fail} failed`);
|
|
1267
866
|
}
|
|
1268
|
-
process.stderr.write(
|
|
867
|
+
process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${statLine}\n`);
|
|
1269
868
|
// Show seal quality score if available
|
|
1270
869
|
if (event.seal_score && typeof event.seal_score.overall === 'number') {
|
|
1271
870
|
const score = event.seal_score.overall;
|
|
1272
871
|
const tier = event.seal_score.tier || '';
|
|
1273
|
-
const scoreColor = score >= 7 ?
|
|
1274
|
-
process.stderr.write(
|
|
872
|
+
const scoreColor = score >= 7 ? chalk_1.default.green : score >= 5 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
873
|
+
process.stderr.write(chalk_1.default.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk_1.default.gray(` (${tier})`) : '') + '\n');
|
|
1275
874
|
}
|
|
1276
875
|
return;
|
|
1277
876
|
}
|
|
@@ -1281,35 +880,40 @@ export class ChatCommand {
|
|
|
1281
880
|
const quality = this.sanitizeServerPath(plan.quality_profile || '');
|
|
1282
881
|
const status = typeof plan.status === 'string' ? plan.status : '';
|
|
1283
882
|
const summary = typeof plan.summary === 'string' ? this.sanitizeServerPath(plan.summary) : '';
|
|
883
|
+
if (status === 'planning') {
|
|
884
|
+
const elapsed = Number.isFinite(Number(plan.elapsed_seconds)) ? Number(plan.elapsed_seconds) : 0;
|
|
885
|
+
spinner.text = elapsed > 0 ? `Planning... (${elapsed}s elapsed)` : 'Planning...';
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
1284
888
|
if (spinner.isSpinning)
|
|
1285
889
|
spinner.stop();
|
|
1286
|
-
process.stderr.write(
|
|
890
|
+
process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
|
|
1287
891
|
if (quality)
|
|
1288
|
-
process.stderr.write(
|
|
892
|
+
process.stderr.write(chalk_1.default.gray(` (${quality})`));
|
|
1289
893
|
process.stderr.write('\n');
|
|
1290
894
|
if (summary) {
|
|
1291
|
-
process.stderr.write(
|
|
895
|
+
process.stderr.write(chalk_1.default.gray(` ${summary}\n`));
|
|
1292
896
|
}
|
|
1293
897
|
if (status === 'planning' && Number.isFinite(Number(plan.elapsed_seconds))) {
|
|
1294
|
-
process.stderr.write(
|
|
898
|
+
process.stderr.write(chalk_1.default.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
|
|
1295
899
|
}
|
|
1296
900
|
if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
|
|
1297
|
-
process.stderr.write(
|
|
901
|
+
process.stderr.write(chalk_1.default.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
|
|
1298
902
|
for (const t of plan.tasks.slice(0, 10)) {
|
|
1299
903
|
const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.map((target) => this.sanitizeServerPath(String(target))).join(', ')}` : '';
|
|
1300
|
-
process.stderr.write(
|
|
904
|
+
process.stderr.write(chalk_1.default.gray(` • ${this.sanitizeServerPath(String(t.title || t.id))}${targets}\n`));
|
|
1301
905
|
}
|
|
1302
906
|
if (plan.tasks.length > 10) {
|
|
1303
|
-
process.stderr.write(
|
|
907
|
+
process.stderr.write(chalk_1.default.gray(` ... and ${plan.tasks.length - 10} more\n`));
|
|
1304
908
|
}
|
|
1305
909
|
}
|
|
1306
910
|
if (Array.isArray(plan.notes) && plan.notes.length > 0) {
|
|
1307
911
|
for (const note of plan.notes.slice(0, 3)) {
|
|
1308
|
-
process.stderr.write(
|
|
912
|
+
process.stderr.write(chalk_1.default.gray(` note: ${this.sanitizeServerPath(String(note))}\n`));
|
|
1309
913
|
}
|
|
1310
914
|
}
|
|
1311
915
|
if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
|
|
1312
|
-
process.stderr.write(
|
|
916
|
+
process.stderr.write(chalk_1.default.gray(` Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}\n`));
|
|
1313
917
|
}
|
|
1314
918
|
spinner.start();
|
|
1315
919
|
spinner.text = status === 'planning' ? 'Planning...' : 'Executing plan...';
|
|
@@ -1318,7 +922,7 @@ export class ChatCommand {
|
|
|
1318
922
|
if (event.type === 'executor_start') {
|
|
1319
923
|
if (spinner.isSpinning)
|
|
1320
924
|
spinner.stop();
|
|
1321
|
-
process.stderr.write(
|
|
925
|
+
process.stderr.write(chalk_1.default.cyan(' [Executor] ') + `Starting ${this.sanitizeServerPath(String(event.task_id || 'task'))}${event.title ? ` - ${this.sanitizeServerPath(String(event.title))}` : ''}
|
|
1322
926
|
`);
|
|
1323
927
|
spinner.start();
|
|
1324
928
|
spinner.text = 'Vigthoria Executor running...';
|
|
@@ -1327,8 +931,8 @@ export class ChatCommand {
|
|
|
1327
931
|
if (event.type === 'executor_error') {
|
|
1328
932
|
if (spinner.isSpinning)
|
|
1329
933
|
spinner.stop();
|
|
1330
|
-
const msg = sanitizeUserFacingErrorText(String(event.error || 'Executor error')) || 'Executor error';
|
|
1331
|
-
process.stderr.write(
|
|
934
|
+
const msg = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.error || 'Executor error')) || 'Executor error';
|
|
935
|
+
process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${msg}
|
|
1332
936
|
`);
|
|
1333
937
|
spinner.start();
|
|
1334
938
|
spinner.text = 'Recovering executor...';
|
|
@@ -1341,11 +945,11 @@ export class ChatCommand {
|
|
|
1341
945
|
const status = String(summary.status || 'completed');
|
|
1342
946
|
const changed = Array.isArray(summary.changed_files) ? summary.changed_files.length : 0;
|
|
1343
947
|
if (status === 'failed') {
|
|
1344
|
-
process.stderr.write(
|
|
948
|
+
process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor task failed${summary.task_id ? ` (${summary.task_id})` : ''}.
|
|
1345
949
|
`);
|
|
1346
950
|
}
|
|
1347
951
|
else {
|
|
1348
|
-
process.stderr.write(
|
|
952
|
+
process.stderr.write(chalk_1.default.green(' [Executor] ') + `Task completed${summary.task_id ? ` (${summary.task_id})` : ''}${changed ? `, ${changed} files changed` : ''}.
|
|
1349
953
|
`);
|
|
1350
954
|
}
|
|
1351
955
|
spinner.start();
|
|
@@ -1355,11 +959,11 @@ export class ChatCommand {
|
|
|
1355
959
|
if (event.type === 'file_mutation') {
|
|
1356
960
|
const rawPath = typeof event.path === 'string' ? this.sanitizeServerPath(event.path) : '';
|
|
1357
961
|
const filePath = rawPath ? rawPath.replace(/\\/g, '/').split('/').slice(-2).join('/') : '';
|
|
1358
|
-
const action = event.action === 'delete' ?
|
|
962
|
+
const action = event.action === 'delete' ? chalk_1.default.red('deleted') : chalk_1.default.green('wrote');
|
|
1359
963
|
if (filePath) {
|
|
1360
964
|
if (spinner.isSpinning)
|
|
1361
965
|
spinner.stop();
|
|
1362
|
-
process.stderr.write(
|
|
966
|
+
process.stderr.write(chalk_1.default.cyan(' [File] ') + `${action} ${filePath}\n`);
|
|
1363
967
|
spinner.start();
|
|
1364
968
|
}
|
|
1365
969
|
return;
|
|
@@ -1368,23 +972,23 @@ export class ChatCommand {
|
|
|
1368
972
|
if (event.checkpointed) {
|
|
1369
973
|
if (spinner.isSpinning)
|
|
1370
974
|
spinner.stop();
|
|
1371
|
-
process.stderr.write(
|
|
975
|
+
process.stderr.write(chalk_1.default.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
|
|
1372
976
|
spinner.start();
|
|
1373
977
|
}
|
|
1374
978
|
else {
|
|
1375
979
|
if (spinner.isSpinning)
|
|
1376
980
|
spinner.stop();
|
|
1377
|
-
const message = sanitizeUserFacingErrorText(String(event.message || 'Agent error')) || 'Agent error';
|
|
981
|
+
const message = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.message || 'Agent error')) || 'Agent error';
|
|
1378
982
|
const plannerLike = /plan|planner|dependency graph/i.test(message);
|
|
1379
983
|
const executorLike = /executor|task failed|iteration/i.test(message);
|
|
1380
984
|
if (plannerLike) {
|
|
1381
|
-
process.stderr.write(
|
|
985
|
+
process.stderr.write(chalk_1.default.red(' [Planner] ') + `Vigthoria Planner encountered an issue: ${this.sanitizeServerPath(message)}\n`);
|
|
1382
986
|
}
|
|
1383
987
|
else if (executorLike) {
|
|
1384
|
-
process.stderr.write(
|
|
988
|
+
process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${this.sanitizeServerPath(message)}\n`);
|
|
1385
989
|
}
|
|
1386
990
|
else {
|
|
1387
|
-
process.stderr.write(
|
|
991
|
+
process.stderr.write(chalk_1.default.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
|
|
1388
992
|
}
|
|
1389
993
|
}
|
|
1390
994
|
return;
|
|
@@ -1392,7 +996,7 @@ export class ChatCommand {
|
|
|
1392
996
|
if (event.type === 'context') {
|
|
1393
997
|
if (spinner.isSpinning)
|
|
1394
998
|
spinner.stop();
|
|
1395
|
-
process.stderr.write(
|
|
999
|
+
process.stderr.write(chalk_1.default.cyan(' [Context] ') + 'Workspace bound\n');
|
|
1396
1000
|
spinner.start();
|
|
1397
1001
|
spinner.text = 'Starting agent...';
|
|
1398
1002
|
return;
|
|
@@ -1402,7 +1006,7 @@ export class ChatCommand {
|
|
|
1402
1006
|
this.v3ToolCallCount = 0;
|
|
1403
1007
|
if (spinner.isSpinning)
|
|
1404
1008
|
spinner.stop();
|
|
1405
|
-
process.stderr.write(
|
|
1009
|
+
process.stderr.write(chalk_1.default.cyan(' [Start] ') + 'Agent initialized\n');
|
|
1406
1010
|
spinner.start();
|
|
1407
1011
|
spinner.text = 'Working...';
|
|
1408
1012
|
return;
|
|
@@ -1416,7 +1020,7 @@ export class ChatCommand {
|
|
|
1416
1020
|
if (spinner.isSpinning)
|
|
1417
1021
|
spinner.stop();
|
|
1418
1022
|
if (fallbackMessage) {
|
|
1419
|
-
process.stderr.write(
|
|
1023
|
+
process.stderr.write(chalk_1.default.cyan(' [V3] ') + `${fallbackMessage}\n`);
|
|
1420
1024
|
}
|
|
1421
1025
|
spinner.start();
|
|
1422
1026
|
spinner.text = fallbackStatus || fallbackStage || 'Working...';
|
|
@@ -1430,7 +1034,7 @@ export class ChatCommand {
|
|
|
1430
1034
|
if (event.type === 'started') {
|
|
1431
1035
|
if (spinner.isSpinning)
|
|
1432
1036
|
spinner.stop();
|
|
1433
|
-
process.stderr.write(
|
|
1037
|
+
process.stderr.write(chalk_1.default.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
|
|
1434
1038
|
spinner.start();
|
|
1435
1039
|
spinner.text = 'Connecting...';
|
|
1436
1040
|
return;
|
|
@@ -1438,7 +1042,7 @@ export class ChatCommand {
|
|
|
1438
1042
|
if (event.type === 'connected') {
|
|
1439
1043
|
if (spinner.isSpinning)
|
|
1440
1044
|
spinner.stop();
|
|
1441
|
-
process.stderr.write(
|
|
1045
|
+
process.stderr.write(chalk_1.default.green(' ✓') + ' Connected to BMAD stream\n');
|
|
1442
1046
|
spinner.start();
|
|
1443
1047
|
spinner.text = 'Working...';
|
|
1444
1048
|
return;
|
|
@@ -1447,7 +1051,7 @@ export class ChatCommand {
|
|
|
1447
1051
|
const agentName = event.agent || 'BMAD agent';
|
|
1448
1052
|
if (spinner.isSpinning)
|
|
1449
1053
|
spinner.stop();
|
|
1450
|
-
process.stderr.write(
|
|
1054
|
+
process.stderr.write(chalk_1.default.cyan(` [Agent] `) + agentName + '\n');
|
|
1451
1055
|
spinner.start();
|
|
1452
1056
|
spinner.text = `Running ${agentName}...`;
|
|
1453
1057
|
return;
|
|
@@ -1457,7 +1061,7 @@ export class ChatCommand {
|
|
|
1457
1061
|
const statusText = `${event.status || 'Working'}${progress}`;
|
|
1458
1062
|
if (spinner.isSpinning)
|
|
1459
1063
|
spinner.stop();
|
|
1460
|
-
process.stderr.write(
|
|
1064
|
+
process.stderr.write(chalk_1.default.cyan(' [Status] ') + statusText + '\n');
|
|
1461
1065
|
spinner.start();
|
|
1462
1066
|
spinner.text = statusText;
|
|
1463
1067
|
return;
|
|
@@ -1465,15 +1069,15 @@ export class ChatCommand {
|
|
|
1465
1069
|
if (event.type === 'result') {
|
|
1466
1070
|
if (spinner.isSpinning)
|
|
1467
1071
|
spinner.stop();
|
|
1468
|
-
process.stderr.write(
|
|
1072
|
+
process.stderr.write(chalk_1.default.green('\n ✓ Operator workflow complete\n'));
|
|
1469
1073
|
return;
|
|
1470
1074
|
}
|
|
1471
1075
|
}
|
|
1472
1076
|
constructor(config, logger) {
|
|
1473
1077
|
this.config = config;
|
|
1474
1078
|
this.logger = logger;
|
|
1475
|
-
this.api = new APIClient(config, logger);
|
|
1476
|
-
this.sessionManager = new SessionManager();
|
|
1079
|
+
this.api = new api_js_1.APIClient(config, logger);
|
|
1080
|
+
this.sessionManager = new session_js_1.SessionManager();
|
|
1477
1081
|
}
|
|
1478
1082
|
async run(options) {
|
|
1479
1083
|
if (!this.config.isAuthenticated()) {
|
|
@@ -1486,7 +1090,6 @@ export class ChatCommand {
|
|
|
1486
1090
|
}
|
|
1487
1091
|
return;
|
|
1488
1092
|
}
|
|
1489
|
-
await this.config.refreshHubModelPreferences().catch(() => null);
|
|
1490
1093
|
this.agentMode = options.agent === true;
|
|
1491
1094
|
this.operatorMode = options.operator === true;
|
|
1492
1095
|
this.workflowTarget = typeof options.workflow === 'string' && options.workflow.trim()
|
|
@@ -1500,17 +1103,30 @@ export class ChatCommand {
|
|
|
1500
1103
|
this.currentModel = this.resolveInitialModel(options);
|
|
1501
1104
|
this.applyNoAgentGovernance(String(options.model || this.currentModel || ''));
|
|
1502
1105
|
this.currentProjectPath = this.resolveProjectPath(options);
|
|
1106
|
+
if ((this.agentMode || this.operatorMode) && (0, agent_session_menu_js_1.shouldShowAgentSessionMenu)(options)) {
|
|
1107
|
+
const sessionConfig = await (0, agent_session_menu_js_1.runAgentSessionMenu)({
|
|
1108
|
+
workspacePath: this.currentProjectPath,
|
|
1109
|
+
autoApprove: this.autoApprove,
|
|
1110
|
+
});
|
|
1111
|
+
this.currentProjectPath = sessionConfig.workspacePath;
|
|
1112
|
+
this.autoApprove = sessionConfig.autoApprove || this.autoApprove;
|
|
1113
|
+
options.autoApprove = this.autoApprove;
|
|
1114
|
+
if (sessionConfig.debugMode) {
|
|
1115
|
+
process.env.DEBUG = process.env.DEBUG || 'vigthoria:*';
|
|
1116
|
+
}
|
|
1117
|
+
console.log(chalk_1.default.green(`Using workspace: ${this.currentProjectPath}`));
|
|
1118
|
+
}
|
|
1503
1119
|
if (this.jsonOutput && !options.prompt) {
|
|
1504
1120
|
throw new Error('--json is only supported together with --prompt.');
|
|
1505
1121
|
}
|
|
1506
1122
|
this.ensureProjectWorkspace();
|
|
1507
1123
|
this.directPromptMode = Boolean(options.prompt);
|
|
1508
1124
|
this.directToolContinuationCount = 0;
|
|
1509
|
-
this.tools = new AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
|
|
1125
|
+
this.tools = new tools_js_1.AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
|
|
1510
1126
|
this.initializeSession(options.resume === true);
|
|
1511
1127
|
// ── Commando Bridge: connect if --bridge was specified ──────────
|
|
1512
1128
|
if (options.bridge) {
|
|
1513
|
-
const bridgeClient = new BridgeClient({
|
|
1129
|
+
const bridgeClient = new bridge_client_js_1.BridgeClient({
|
|
1514
1130
|
bridgeUrl: options.bridge,
|
|
1515
1131
|
apiKey: this.config.get('authToken'),
|
|
1516
1132
|
onAdminCommand: (cmd) => this.handleAdminCommand(cmd),
|
|
@@ -1545,7 +1161,7 @@ export class ChatCommand {
|
|
|
1545
1161
|
const timeoutId = options.bridge && bridgePromptTimeoutMs > 0
|
|
1546
1162
|
? setTimeout(() => {
|
|
1547
1163
|
timedOut = true;
|
|
1548
|
-
const b = getBridgeClient();
|
|
1164
|
+
const b = (0, bridge_client_js_1.getBridgeClient)();
|
|
1549
1165
|
if (b) {
|
|
1550
1166
|
b.emitEnd({ reason: 'timeout' });
|
|
1551
1167
|
b.destroy();
|
|
@@ -1560,7 +1176,7 @@ export class ChatCommand {
|
|
|
1560
1176
|
if (timeoutId)
|
|
1561
1177
|
clearTimeout(timeoutId);
|
|
1562
1178
|
if (!timedOut) {
|
|
1563
|
-
const bridge = getBridgeClient();
|
|
1179
|
+
const bridge = (0, bridge_client_js_1.getBridgeClient)();
|
|
1564
1180
|
if (bridge) {
|
|
1565
1181
|
bridge.emitEnd({ reason: 'prompt-complete' });
|
|
1566
1182
|
bridge.destroy();
|
|
@@ -1572,7 +1188,7 @@ export class ChatCommand {
|
|
|
1572
1188
|
process.exit(process.exitCode ?? 0);
|
|
1573
1189
|
}
|
|
1574
1190
|
await this.startInteractiveChat();
|
|
1575
|
-
const bridge = getBridgeClient();
|
|
1191
|
+
const bridge = (0, bridge_client_js_1.getBridgeClient)();
|
|
1576
1192
|
if (bridge) {
|
|
1577
1193
|
bridge.emitEnd({ reason: 'interactive-exit' });
|
|
1578
1194
|
bridge.destroy();
|
|
@@ -1582,24 +1198,24 @@ export class ChatCommand {
|
|
|
1582
1198
|
handleAdminCommand(cmd) {
|
|
1583
1199
|
switch (cmd.action) {
|
|
1584
1200
|
case 'ping':
|
|
1585
|
-
getBridgeClient()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
|
|
1201
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
|
|
1586
1202
|
break;
|
|
1587
1203
|
case 'set-model':
|
|
1588
1204
|
if (cmd.params?.value && typeof cmd.params.value === 'string') {
|
|
1589
1205
|
this.currentModel = cmd.params.value;
|
|
1590
|
-
getBridgeClient()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
|
|
1206
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
|
|
1591
1207
|
if (!this.jsonOutput)
|
|
1592
|
-
console.log(
|
|
1208
|
+
console.log(chalk_1.default.yellow(`[bridge] Model changed to: ${this.currentModel}`));
|
|
1593
1209
|
}
|
|
1594
1210
|
break;
|
|
1595
1211
|
case 'abort':
|
|
1596
1212
|
if (!this.jsonOutput)
|
|
1597
|
-
console.log(
|
|
1598
|
-
getBridgeClient()?.emitEnd({ reason: 'admin-abort' });
|
|
1213
|
+
console.log(chalk_1.default.red(`[bridge] Abort requested by admin`));
|
|
1214
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'admin-abort' });
|
|
1599
1215
|
process.exit(0);
|
|
1600
1216
|
break;
|
|
1601
1217
|
default:
|
|
1602
|
-
getBridgeClient()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
|
|
1218
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
|
|
1603
1219
|
}
|
|
1604
1220
|
}
|
|
1605
1221
|
ensureProjectWorkspace() {
|
|
@@ -1635,13 +1251,13 @@ export class ChatCommand {
|
|
|
1635
1251
|
// Guardrail: do not watch broad home/root scopes on interactive shells.
|
|
1636
1252
|
if (normalized.toLowerCase() === normalizedHome.toLowerCase()) {
|
|
1637
1253
|
if (!this.jsonOutput) {
|
|
1638
|
-
console.log(
|
|
1254
|
+
console.log(chalk_1.default.gray('Info: workspace watcher disabled for home directory scope.'));
|
|
1639
1255
|
}
|
|
1640
1256
|
return false;
|
|
1641
1257
|
}
|
|
1642
1258
|
if (/^[a-zA-Z]:\/$/.test(normalized) || normalized === '/') {
|
|
1643
1259
|
if (!this.jsonOutput) {
|
|
1644
|
-
console.log(
|
|
1260
|
+
console.log(chalk_1.default.gray('Info: workspace watcher disabled for filesystem root scope.'));
|
|
1645
1261
|
}
|
|
1646
1262
|
return false;
|
|
1647
1263
|
}
|
|
@@ -1657,7 +1273,7 @@ export class ChatCommand {
|
|
|
1657
1273
|
const explicitPath = this.resolvePromptWorkspacePath(options.prompt, process.cwd());
|
|
1658
1274
|
if (explicitPath) {
|
|
1659
1275
|
if (!this.jsonOutput) {
|
|
1660
|
-
console.log(
|
|
1276
|
+
console.log(chalk_1.default.gray(`📁 Using project path from prompt: ${explicitPath}`));
|
|
1661
1277
|
}
|
|
1662
1278
|
return explicitPath;
|
|
1663
1279
|
}
|
|
@@ -1759,7 +1375,7 @@ export class ChatCommand {
|
|
|
1759
1375
|
return path.join(rootPath, `${folderName}-${Date.now()}`);
|
|
1760
1376
|
}
|
|
1761
1377
|
initializeSession(resume) {
|
|
1762
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
1378
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
1763
1379
|
if (resume) {
|
|
1764
1380
|
this.currentSession = this.sessionManager.getLatest(this.currentProjectPath);
|
|
1765
1381
|
if (this.currentSession) {
|
|
@@ -1805,11 +1421,11 @@ export class ChatCommand {
|
|
|
1805
1421
|
// Suppress all setup banners in direct-prompt mode so only the final
|
|
1806
1422
|
// answer reaches stdout. Interactive (REPL) mode still shows them.
|
|
1807
1423
|
if (!this.jsonOutput) {
|
|
1808
|
-
console.log(
|
|
1809
|
-
console.log(
|
|
1810
|
-
console.log(
|
|
1424
|
+
console.log(chalk_1.default.cyan('Running single prompt in direct mode.'));
|
|
1425
|
+
console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
|
|
1426
|
+
console.log(chalk_1.default.gray(`Project: ${this.currentProjectPath}`));
|
|
1811
1427
|
if (this.workflowTarget) {
|
|
1812
|
-
console.log(
|
|
1428
|
+
console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
|
|
1813
1429
|
}
|
|
1814
1430
|
console.log();
|
|
1815
1431
|
}
|
|
@@ -1873,7 +1489,7 @@ export class ChatCommand {
|
|
|
1873
1489
|
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
1874
1490
|
const resolvedWorkflow = await this.callApi('Resolve VigFlow workflow', () => this.api.resolveVigFlowWorkflow(selector));
|
|
1875
1491
|
const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
|
|
1876
|
-
const spinner = this.jsonOutput ? null : createSpinner({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
|
|
1492
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
|
|
1877
1493
|
try {
|
|
1878
1494
|
const execution = await this.callApi('Run VigFlow workflow', () => this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
|
|
1879
1495
|
data: {
|
|
@@ -1922,9 +1538,9 @@ export class ChatCommand {
|
|
|
1922
1538
|
return;
|
|
1923
1539
|
}
|
|
1924
1540
|
this.logger.success(`Workflow target ${resolvedWorkflow.name} ${execution.status}`);
|
|
1925
|
-
console.log(
|
|
1926
|
-
console.log(
|
|
1927
|
-
console.log(
|
|
1541
|
+
console.log(chalk_1.default.gray(`Workflow ID: ${resolvedWorkflow.id}`));
|
|
1542
|
+
console.log(chalk_1.default.gray(`Execution ID: ${execution.executionId}`));
|
|
1543
|
+
console.log(chalk_1.default.gray(`Mode: ${invocationMode}`));
|
|
1928
1544
|
if (content) {
|
|
1929
1545
|
console.log(content);
|
|
1930
1546
|
}
|
|
@@ -1967,9 +1583,9 @@ export class ChatCommand {
|
|
|
1967
1583
|
await this.runLocalAgentLoop(prompt);
|
|
1968
1584
|
return;
|
|
1969
1585
|
}
|
|
1970
|
-
getBridgeClient()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
|
|
1586
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
|
|
1971
1587
|
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
1972
|
-
const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
|
|
1588
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
|
|
1973
1589
|
const workflowType = 'full';
|
|
1974
1590
|
const executionPrompt = this.buildExecutionPrompt(prompt);
|
|
1975
1591
|
try {
|
|
@@ -1997,7 +1613,7 @@ export class ChatCommand {
|
|
|
1997
1613
|
const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(responseText)
|
|
1998
1614
|
|| (responseText.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(responseText));
|
|
1999
1615
|
if (isPolicyAck) {
|
|
2000
|
-
throw new CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
|
|
1616
|
+
throw new api_js_1.CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
|
|
2001
1617
|
}
|
|
2002
1618
|
if (this.jsonOutput) {
|
|
2003
1619
|
console.log(JSON.stringify({
|
|
@@ -2013,7 +1629,7 @@ export class ChatCommand {
|
|
|
2013
1629
|
else {
|
|
2014
1630
|
console.log(response.content || 'Operator workflow completed.');
|
|
2015
1631
|
if (response.savedWorkflow?.id) {
|
|
2016
|
-
console.log(
|
|
1632
|
+
console.log(chalk_1.default.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
|
|
2017
1633
|
}
|
|
2018
1634
|
}
|
|
2019
1635
|
this.rememberBrainEvent('validation', `GoA operator workflow completed${response.workflowId ? ` workflow ${response.workflowId}` : ''}${response.savedWorkflow?.id ? ` saved VigFlow ${response.savedWorkflow.id}` : ''}.`, 'operator');
|
|
@@ -2024,8 +1640,8 @@ export class ChatCommand {
|
|
|
2024
1640
|
if (spinner) {
|
|
2025
1641
|
spinner.stop();
|
|
2026
1642
|
}
|
|
2027
|
-
const cliErr = error instanceof CLIError ? error : classifyError(error);
|
|
2028
|
-
const errorMsg = formatCLIError(cliErr);
|
|
1643
|
+
const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
|
|
1644
|
+
const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
|
|
2029
1645
|
if (!this.jsonOutput) {
|
|
2030
1646
|
this.logger.error('Operator workflow failed');
|
|
2031
1647
|
}
|
|
@@ -2052,8 +1668,8 @@ export class ChatCommand {
|
|
|
2052
1668
|
* BMAD orchestrator to scan the workspace.
|
|
2053
1669
|
*/
|
|
2054
1670
|
async runOperatorDirectAnswer(prompt) {
|
|
2055
|
-
getBridgeClient()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
|
|
2056
|
-
const spinner = this.jsonOutput ? null : createSpinner({ text: 'Operator (direct)...', spinner: 'clock' }).start();
|
|
1671
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
|
|
1672
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Operator (direct)...', spinner: 'clock' }).start();
|
|
2057
1673
|
try {
|
|
2058
1674
|
let operatorGrounding = [
|
|
2059
1675
|
'You are Vigthoria Operator, a DevOps and infrastructure analysis assistant.',
|
|
@@ -2085,7 +1701,7 @@ export class ChatCommand {
|
|
|
2085
1701
|
const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(content)
|
|
2086
1702
|
|| (content.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(content));
|
|
2087
1703
|
if (isPolicyAck || !content) {
|
|
2088
|
-
throw new CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
|
|
1704
|
+
throw new api_js_1.CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
|
|
2089
1705
|
}
|
|
2090
1706
|
if (this.jsonOutput) {
|
|
2091
1707
|
console.log(JSON.stringify({
|
|
@@ -2105,8 +1721,8 @@ export class ChatCommand {
|
|
|
2105
1721
|
catch (error) {
|
|
2106
1722
|
if (spinner)
|
|
2107
1723
|
spinner.stop();
|
|
2108
|
-
const cliErr = error instanceof CLIError ? error : classifyError(error);
|
|
2109
|
-
const errorMsg = formatCLIError(cliErr);
|
|
1724
|
+
const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
|
|
1725
|
+
const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
|
|
2110
1726
|
if (this.jsonOutput) {
|
|
2111
1727
|
process.exitCode = 1;
|
|
2112
1728
|
console.log(JSON.stringify({
|
|
@@ -2138,8 +1754,8 @@ export class ChatCommand {
|
|
|
2138
1754
|
this.currentSession.agentMode = true;
|
|
2139
1755
|
this.currentSession.model = this.currentModel;
|
|
2140
1756
|
}
|
|
2141
|
-
console.log(
|
|
2142
|
-
console.log(
|
|
1757
|
+
console.log(chalk_1.default.yellow('This request needs file access, so I am switching to Agent mode and working in this workspace now.'));
|
|
1758
|
+
console.log(chalk_1.default.gray('You do not need to confirm with "yes"; I will continue until the agent run finishes or reports a blocker.'));
|
|
2143
1759
|
await this.runAgentTurn(promptToRun);
|
|
2144
1760
|
return;
|
|
2145
1761
|
}
|
|
@@ -2198,8 +1814,8 @@ export class ChatCommand {
|
|
|
2198
1814
|
}
|
|
2199
1815
|
}
|
|
2200
1816
|
this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
|
|
2201
|
-
getBridgeClient()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
|
|
2202
|
-
const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking...', spinner: 'clock' }).start();
|
|
1817
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
|
|
1818
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
|
|
2203
1819
|
try {
|
|
2204
1820
|
const response = await this.callApi('Send chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel));
|
|
2205
1821
|
if (spinner)
|
|
@@ -2223,7 +1839,7 @@ export class ChatCommand {
|
|
|
2223
1839
|
console.log(finalText);
|
|
2224
1840
|
}
|
|
2225
1841
|
else {
|
|
2226
|
-
console.log(
|
|
1842
|
+
console.log(chalk_1.default.yellow('The model returned an empty response. Try rephrasing your question, or use --agent mode for grounded repo analysis.'));
|
|
2227
1843
|
}
|
|
2228
1844
|
this.messages.push({ role: 'assistant', content: response.message || '' });
|
|
2229
1845
|
this.saveSession();
|
|
@@ -2231,8 +1847,8 @@ export class ChatCommand {
|
|
|
2231
1847
|
catch (error) {
|
|
2232
1848
|
if (spinner)
|
|
2233
1849
|
spinner.stop();
|
|
2234
|
-
const cliErr = error instanceof CLIError ? error : classifyError(error);
|
|
2235
|
-
const errorMsg = formatCLIError(cliErr);
|
|
1850
|
+
const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
|
|
1851
|
+
const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
|
|
2236
1852
|
if (this.jsonOutput) {
|
|
2237
1853
|
process.exitCode = 1;
|
|
2238
1854
|
console.log(JSON.stringify({
|
|
@@ -2249,11 +1865,10 @@ export class ChatCommand {
|
|
|
2249
1865
|
}
|
|
2250
1866
|
}
|
|
2251
1867
|
}
|
|
2252
|
-
async runAgentTurn(prompt
|
|
1868
|
+
async runAgentTurn(prompt) {
|
|
2253
1869
|
if (!this.tools) {
|
|
2254
1870
|
throw new Error('Agent tools are not initialized.');
|
|
2255
1871
|
}
|
|
2256
|
-
this.bindPromptWorkspace(prompt);
|
|
2257
1872
|
const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
|
|
2258
1873
|
const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(prompt);
|
|
2259
1874
|
if (handledByDirectFileFlow) {
|
|
@@ -2264,17 +1879,11 @@ export class ChatCommand {
|
|
|
2264
1879
|
// bypassed (e.g. HTML files routed to V3) so the local agent loop has file
|
|
2265
1880
|
// awareness and the ⚙ Executing: read_file banner is always emitted.
|
|
2266
1881
|
await this.primeBypassedTargetFileContext(prompt);
|
|
2267
|
-
if (
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
this.saveSession();
|
|
2271
|
-
return;
|
|
2272
|
-
}
|
|
2273
|
-
if (!this.jsonOutput) {
|
|
2274
|
-
console.log(chalk.yellow('Local chat backend unavailable (self-hosted inference may be offline). Trying V3 agent workflow...'));
|
|
2275
|
-
}
|
|
1882
|
+
if (this.shouldPreferLocalAgentLoop(prompt)) {
|
|
1883
|
+
await this.runLocalAgentLoop(prompt);
|
|
1884
|
+
return;
|
|
2276
1885
|
}
|
|
2277
|
-
const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt
|
|
1886
|
+
const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt);
|
|
2278
1887
|
if (handledByV3Workflow) {
|
|
2279
1888
|
this.saveSession();
|
|
2280
1889
|
return;
|
|
@@ -2288,108 +1897,24 @@ export class ChatCommand {
|
|
|
2288
1897
|
}
|
|
2289
1898
|
await this.runLocalAgentLoop(prompt);
|
|
2290
1899
|
}
|
|
2291
|
-
localAgentIterationCount = 0;
|
|
2292
|
-
buildLocalAgentChatOptions(preflight, onRouteAttempt) {
|
|
2293
|
-
const preferredRoute = preflight?.endpoint
|
|
2294
|
-
? this.api.mapPreflightEndpointToRoute(preflight.endpoint)
|
|
2295
|
-
: undefined;
|
|
2296
|
-
return {
|
|
2297
|
-
fastFail: true,
|
|
2298
|
-
singleRoute: true,
|
|
2299
|
-
stream: true,
|
|
2300
|
-
connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
|
|
2301
|
-
idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
|
|
2302
|
-
preferredRoute: preferredRoute || 'coder',
|
|
2303
|
-
onRouteAttempt,
|
|
2304
|
-
};
|
|
2305
|
-
}
|
|
2306
|
-
printChatModelPreflight(preflight) {
|
|
2307
|
-
if (this.jsonOutput) {
|
|
2308
|
-
return;
|
|
2309
|
-
}
|
|
2310
|
-
console.log(chalk.gray('Model backend preflight:'));
|
|
2311
|
-
for (const route of preflight.routes) {
|
|
2312
|
-
const marker = route.ok ? chalk.green(' ✓') : chalk.red(' ✗');
|
|
2313
|
-
const detail = route.error ? chalk.gray(` — ${route.error}`) : '';
|
|
2314
|
-
console.log(`${marker} ${route.name}${detail}`);
|
|
2315
|
-
}
|
|
2316
|
-
if (preflight.healthy) {
|
|
2317
|
-
console.log(chalk.gray(`Using: ${preflight.endpoint}`));
|
|
2318
|
-
}
|
|
2319
|
-
else if (preflight.error) {
|
|
2320
|
-
console.log(chalk.yellow(preflight.error));
|
|
2321
|
-
}
|
|
2322
|
-
console.log();
|
|
2323
|
-
}
|
|
2324
1900
|
async runLocalAgentLoop(prompt) {
|
|
2325
1901
|
if (!this.tools) {
|
|
2326
1902
|
throw new Error('Agent tools are not initialized.');
|
|
2327
1903
|
}
|
|
2328
|
-
this.localAgentIterationCount = 0;
|
|
2329
|
-
const runtime = this.getRuntimeEnvironmentContext();
|
|
2330
|
-
if (!this.jsonOutput) {
|
|
2331
|
-
console.log();
|
|
2332
|
-
console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
|
|
2333
|
-
console.log(chalk.gray('Reason: local-machine-agent-loop'));
|
|
2334
|
-
console.log(chalk.gray(`Platform: ${runtime.platform}`));
|
|
2335
|
-
console.log(chalk.gray(`Machine scope: ${runtime.machineScope}`));
|
|
2336
|
-
console.log(chalk.gray(`Workspace: ${runtime.workspacePath}`));
|
|
2337
|
-
console.log(chalk.gray('Tools execute on your local filesystem.'));
|
|
2338
|
-
console.log(chalk.gray('━'.repeat(30)));
|
|
2339
|
-
console.log();
|
|
2340
|
-
}
|
|
2341
1904
|
this.lastActionableUserInput = prompt;
|
|
2342
1905
|
this.directToolContinuationCount = 0;
|
|
2343
1906
|
this.agentToolEvidence = { discovery: 0, mutation: 0, searchFailed: 0 };
|
|
2344
1907
|
this.tools.clearSessionApprovals();
|
|
2345
|
-
getBridgeClient()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
|
|
1908
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
|
|
2346
1909
|
this.ensureAgentSystemPrompt();
|
|
2347
1910
|
this.messages.push({ role: 'user', content: this.buildScopedUserPrompt(prompt) });
|
|
2348
1911
|
this.saveSession();
|
|
2349
|
-
const preflightSpinner = this.jsonOutput
|
|
2350
|
-
? null
|
|
2351
|
-
: createSpinner({ text: 'Checking model connection (preflight)...', spinner: 'clock' }).start();
|
|
2352
|
-
let preflight;
|
|
2353
|
-
try {
|
|
2354
|
-
preflight = await this.api.runChatModelPreflight(this.currentModel);
|
|
2355
|
-
}
|
|
2356
|
-
finally {
|
|
2357
|
-
if (preflightSpinner) {
|
|
2358
|
-
preflightSpinner.stop();
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
this.printChatModelPreflight(preflight);
|
|
2362
|
-
if (!preflight.healthy) {
|
|
2363
|
-
return false;
|
|
2364
|
-
}
|
|
2365
1912
|
const maxTurns = 10;
|
|
2366
|
-
const preferredRoute = preflight?.endpoint
|
|
2367
|
-
? (this.api.mapPreflightEndpointToRoute(preflight.endpoint) || 'coder')
|
|
2368
|
-
: 'coder';
|
|
2369
1913
|
for (let turn = 0; turn < maxTurns; turn += 1) {
|
|
2370
|
-
const spinner = this.jsonOutput ? null : createSpinner({ text: turn === 0 ? 'Planning
|
|
2371
|
-
let streamedVisible = '';
|
|
1914
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
|
|
2372
1915
|
let response;
|
|
2373
1916
|
try {
|
|
2374
|
-
response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(
|
|
2375
|
-
fastFail: true,
|
|
2376
|
-
singleRoute: true,
|
|
2377
|
-
stream: true,
|
|
2378
|
-
connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
|
|
2379
|
-
idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
|
|
2380
|
-
preferredRoute,
|
|
2381
|
-
onRouteAttempt: (routeLabel) => {
|
|
2382
|
-
if (spinner) {
|
|
2383
|
-
spinner.text = routeLabel;
|
|
2384
|
-
}
|
|
2385
|
-
},
|
|
2386
|
-
onStreamDelta: (chunk) => {
|
|
2387
|
-
streamedVisible += chunk;
|
|
2388
|
-
if (spinner) {
|
|
2389
|
-
spinner.text = streamedVisible.trim().slice(-72) || 'Streaming model output...';
|
|
2390
|
-
}
|
|
2391
|
-
},
|
|
2392
|
-
}), 0);
|
|
1917
|
+
response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
|
|
2393
1918
|
}
|
|
2394
1919
|
catch (firstErr) {
|
|
2395
1920
|
// If we already gathered evidence and the model API fails on a
|
|
@@ -2398,14 +1923,7 @@ export class ChatCommand {
|
|
|
2398
1923
|
this.logger.debug('Agent continuation API call failed, retrying once...');
|
|
2399
1924
|
try {
|
|
2400
1925
|
await new Promise(r => setTimeout(r, 2000));
|
|
2401
|
-
response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel,
|
|
2402
|
-
fastFail: true,
|
|
2403
|
-
singleRoute: true,
|
|
2404
|
-
stream: true,
|
|
2405
|
-
connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
|
|
2406
|
-
idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
|
|
2407
|
-
preferredRoute,
|
|
2408
|
-
}), 0);
|
|
1926
|
+
response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
|
|
2409
1927
|
}
|
|
2410
1928
|
catch (retryErr) {
|
|
2411
1929
|
// Retry also failed — synthesize an answer from evidence
|
|
@@ -2428,40 +1946,17 @@ export class ChatCommand {
|
|
|
2428
1946
|
console.log(fallbackContent);
|
|
2429
1947
|
}
|
|
2430
1948
|
this.saveSession();
|
|
2431
|
-
return
|
|
1949
|
+
return;
|
|
2432
1950
|
}
|
|
2433
1951
|
throw retryErr;
|
|
2434
1952
|
}
|
|
2435
1953
|
}
|
|
2436
1954
|
else {
|
|
2437
|
-
const cliErr = firstErr instanceof CLIError ? firstErr : classifyError(firstErr);
|
|
2438
|
-
const formatted = formatCLIError(cliErr);
|
|
1955
|
+
const cliErr = firstErr instanceof api_js_1.CLIError ? firstErr : (0, api_js_1.classifyError)(firstErr);
|
|
1956
|
+
const formatted = (0, api_js_1.formatCLIError)(cliErr);
|
|
2439
1957
|
if (spinner)
|
|
2440
1958
|
spinner.stop();
|
|
2441
|
-
this.rememberBrainEvent('issue', 'Agent
|
|
2442
|
-
if (turn === 0 && (cliErr.category === 'model_backend' || cliErr.category === 'network' || cliErr.category === 'timeout')) {
|
|
2443
|
-
if (!this.jsonOutput) {
|
|
2444
|
-
this.logger.error(formatted);
|
|
2445
|
-
const transport = this.api.getLastChatTransportErrors();
|
|
2446
|
-
if (transport.length > 0) {
|
|
2447
|
-
console.log(chalk.gray(`Routes tried: ${transport.slice(0, 3).join(' | ')}`));
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
else {
|
|
2451
|
-
process.exitCode = 1;
|
|
2452
|
-
console.log(JSON.stringify({
|
|
2453
|
-
success: false,
|
|
2454
|
-
mode: 'agent',
|
|
2455
|
-
model: this.currentModel,
|
|
2456
|
-
partial: false,
|
|
2457
|
-
content: '',
|
|
2458
|
-
error: formatted,
|
|
2459
|
-
errorCategory: cliErr.category,
|
|
2460
|
-
metadata: { executionPath: 'local-agent-loop' },
|
|
2461
|
-
}, null, 2));
|
|
2462
|
-
}
|
|
2463
|
-
return false;
|
|
2464
|
-
}
|
|
1959
|
+
this.rememberBrainEvent('issue', 'Agent API preflight failed: ' + formatted, 'agent');
|
|
2465
1960
|
if (this.jsonOutput) {
|
|
2466
1961
|
process.exitCode = 1;
|
|
2467
1962
|
console.log(JSON.stringify({
|
|
@@ -2478,7 +1973,7 @@ export class ChatCommand {
|
|
|
2478
1973
|
else {
|
|
2479
1974
|
this.logger.error(formatted);
|
|
2480
1975
|
}
|
|
2481
|
-
return
|
|
1976
|
+
return;
|
|
2482
1977
|
}
|
|
2483
1978
|
}
|
|
2484
1979
|
if (spinner)
|
|
@@ -2488,12 +1983,7 @@ export class ChatCommand {
|
|
|
2488
1983
|
this.messages.push({ role: 'assistant', content: assistantMessage });
|
|
2489
1984
|
const toolCalls = this.extractToolCalls(assistantMessage);
|
|
2490
1985
|
const visibleText = this.stripToolPayloads(assistantMessage).trim();
|
|
2491
|
-
|
|
2492
|
-
this.localAgentIterationCount += 1;
|
|
2493
|
-
console.log(chalk.cyan(`\n── Iteration ${this.localAgentIterationCount} ──`));
|
|
2494
|
-
console.log(chalk.gray(visibleText));
|
|
2495
|
-
}
|
|
2496
|
-
getBridgeClient()?.emitModelResponse({
|
|
1986
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({
|
|
2497
1987
|
model: this.currentModel,
|
|
2498
1988
|
chars: assistantMessage.length,
|
|
2499
1989
|
hasToolCalls: toolCalls.length > 0,
|
|
@@ -2561,7 +2051,7 @@ export class ChatCommand {
|
|
|
2561
2051
|
console.log(finalContent);
|
|
2562
2052
|
}
|
|
2563
2053
|
this.saveSession();
|
|
2564
|
-
return
|
|
2054
|
+
return;
|
|
2565
2055
|
}
|
|
2566
2056
|
await this.executeToolCalls(toolCalls);
|
|
2567
2057
|
this.directToolContinuationCount += 1;
|
|
@@ -2574,8 +2064,8 @@ export class ChatCommand {
|
|
|
2574
2064
|
catch (error) {
|
|
2575
2065
|
if (spinner)
|
|
2576
2066
|
spinner.stop();
|
|
2577
|
-
const cliErr = error instanceof CLIError ? error : classifyError(error);
|
|
2578
|
-
const errorMsg = formatCLIError(cliErr);
|
|
2067
|
+
const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
|
|
2068
|
+
const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
|
|
2579
2069
|
this.rememberBrainEvent('issue', `Agent turn failed: ${errorMsg}`, 'agent');
|
|
2580
2070
|
if (this.jsonOutput) {
|
|
2581
2071
|
process.exitCode = 1;
|
|
@@ -2595,7 +2085,7 @@ export class ChatCommand {
|
|
|
2595
2085
|
else {
|
|
2596
2086
|
this.logger.error(errorMsg);
|
|
2597
2087
|
}
|
|
2598
|
-
return
|
|
2088
|
+
return;
|
|
2599
2089
|
}
|
|
2600
2090
|
}
|
|
2601
2091
|
if (this.jsonOutput) {
|
|
@@ -2616,7 +2106,6 @@ export class ChatCommand {
|
|
|
2616
2106
|
console.log('Task complete.');
|
|
2617
2107
|
}
|
|
2618
2108
|
this.saveSession();
|
|
2619
|
-
return true;
|
|
2620
2109
|
}
|
|
2621
2110
|
async primeBypassedTargetFileContext(prompt) {
|
|
2622
2111
|
if (!this.directPromptMode || !this.tools) {
|
|
@@ -2636,12 +2125,12 @@ export class ChatCommand {
|
|
|
2636
2125
|
args: { path: targetFile },
|
|
2637
2126
|
};
|
|
2638
2127
|
if (!this.jsonOutput) {
|
|
2639
|
-
console.log(
|
|
2128
|
+
console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
|
|
2640
2129
|
}
|
|
2641
2130
|
const readResult = await this.tools.execute(readCall);
|
|
2642
2131
|
const readSummary = this.formatToolResult(readCall, readResult);
|
|
2643
2132
|
if (!this.jsonOutput) {
|
|
2644
|
-
console.log(readResult.success ?
|
|
2133
|
+
console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
|
|
2645
2134
|
}
|
|
2646
2135
|
if (readResult.success && readResult.output) {
|
|
2647
2136
|
this.messages.push({ role: 'system', content: readSummary });
|
|
@@ -2663,12 +2152,12 @@ export class ChatCommand {
|
|
|
2663
2152
|
args: { path: targetFile },
|
|
2664
2153
|
};
|
|
2665
2154
|
if (!this.jsonOutput) {
|
|
2666
|
-
console.log(
|
|
2155
|
+
console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
|
|
2667
2156
|
}
|
|
2668
2157
|
const readResult = await this.tools.execute(readCall);
|
|
2669
2158
|
const readSummary = this.formatToolResult(readCall, readResult);
|
|
2670
2159
|
if (!this.jsonOutput) {
|
|
2671
|
-
console.log(readResult.success ?
|
|
2160
|
+
console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
|
|
2672
2161
|
}
|
|
2673
2162
|
this.messages.push({ role: 'system', content: readSummary });
|
|
2674
2163
|
if (!readResult.success || !readResult.output) {
|
|
@@ -2711,12 +2200,12 @@ export class ChatCommand {
|
|
|
2711
2200
|
},
|
|
2712
2201
|
};
|
|
2713
2202
|
if (!this.jsonOutput) {
|
|
2714
|
-
console.log(
|
|
2203
|
+
console.log(chalk_1.default.cyan(`⚙ Executing: ${writeCall.tool}`));
|
|
2715
2204
|
}
|
|
2716
2205
|
const writeResult = await this.tools.execute(writeCall);
|
|
2717
2206
|
const writeSummary = this.formatToolResult(writeCall, writeResult);
|
|
2718
2207
|
if (!this.jsonOutput) {
|
|
2719
|
-
console.log(writeResult.success ?
|
|
2208
|
+
console.log(writeResult.success ? chalk_1.default.gray(writeSummary) : chalk_1.default.red(writeSummary));
|
|
2720
2209
|
}
|
|
2721
2210
|
this.messages.push({ role: 'system', content: writeSummary });
|
|
2722
2211
|
if (!writeResult.success) {
|
|
@@ -2752,10 +2241,10 @@ export class ChatCommand {
|
|
|
2752
2241
|
console.log(`Updated ${targetFile}.`);
|
|
2753
2242
|
if (previewGate.required) {
|
|
2754
2243
|
if (previewGate.passed) {
|
|
2755
|
-
console.log(
|
|
2244
|
+
console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
|
|
2756
2245
|
}
|
|
2757
2246
|
else {
|
|
2758
|
-
console.log(
|
|
2247
|
+
console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
|
|
2759
2248
|
}
|
|
2760
2249
|
}
|
|
2761
2250
|
}
|
|
@@ -2802,11 +2291,14 @@ export class ChatCommand {
|
|
|
2802
2291
|
'Do not reinterpret this confirmation as a new website, landing page, template, or index.html task.',
|
|
2803
2292
|
].join('\n');
|
|
2804
2293
|
}
|
|
2805
|
-
async tryV3AgentWorkflow(prompt
|
|
2294
|
+
async tryV3AgentWorkflow(prompt) {
|
|
2806
2295
|
// Extract explicit workspace path from prompt (if provided by user)
|
|
2807
2296
|
let promptWorkspacePath = null;
|
|
2808
2297
|
try {
|
|
2809
2298
|
promptWorkspacePath = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
|
|
2299
|
+
if (promptWorkspacePath && !this.jsonOutput) {
|
|
2300
|
+
console.log(chalk_1.default.cyan(`📁 Workspace from prompt: ${promptWorkspacePath}`));
|
|
2301
|
+
}
|
|
2810
2302
|
}
|
|
2811
2303
|
catch {
|
|
2812
2304
|
// Path extraction failed, use default workspace
|
|
@@ -2822,15 +2314,15 @@ export class ChatCommand {
|
|
|
2822
2314
|
// STREAMING: Log routing decision transparently to user
|
|
2823
2315
|
if (!this.jsonOutput) {
|
|
2824
2316
|
console.log();
|
|
2825
|
-
console.log(
|
|
2826
|
-
console.log(
|
|
2827
|
-
console.log(
|
|
2828
|
-
console.log(
|
|
2829
|
-
console.log(
|
|
2317
|
+
console.log(chalk_1.default.gray('━━━ ROUTING DECISION ━━━'));
|
|
2318
|
+
console.log(chalk_1.default.gray(`Reason: ${routingPolicy.routeReason}`));
|
|
2319
|
+
console.log(chalk_1.default.gray(`Model: ${routingPolicy.selectedModel}`));
|
|
2320
|
+
console.log(chalk_1.default.gray(`Cloud Eligible: ${routingPolicy.cloudEligible}`));
|
|
2321
|
+
console.log(chalk_1.default.gray(`Cloud Selected: ${routingPolicy.cloudSelected}`));
|
|
2830
2322
|
if (routingPolicy.heavyTask) {
|
|
2831
|
-
console.log(
|
|
2323
|
+
console.log(chalk_1.default.gray(`Task Complexity: HEAVY`));
|
|
2832
2324
|
}
|
|
2833
|
-
console.log(
|
|
2325
|
+
console.log(chalk_1.default.gray('━'.repeat(30)));
|
|
2834
2326
|
console.log();
|
|
2835
2327
|
}
|
|
2836
2328
|
// Reset streaming counters for new workflow
|
|
@@ -2838,12 +2330,9 @@ export class ChatCommand {
|
|
|
2838
2330
|
this.v3ToolCallCount = 0;
|
|
2839
2331
|
this.v3LastActivity = Date.now();
|
|
2840
2332
|
this.v3StreamingStarted = false;
|
|
2841
|
-
|
|
2842
|
-
this.v3LiveToolEvidence = [];
|
|
2843
|
-
this.v3PendingToolCalls = [];
|
|
2844
|
-
const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
|
|
2333
|
+
const taskDisplay = new task_display_js_1.TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
|
|
2845
2334
|
taskDisplay.start(0);
|
|
2846
|
-
const spinner = this.jsonOutput ? null : createSpinner({
|
|
2335
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
|
|
2847
2336
|
text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
|
|
2848
2337
|
spinner: 'clock',
|
|
2849
2338
|
}).start();
|
|
@@ -2902,41 +2391,20 @@ export class ChatCommand {
|
|
|
2902
2391
|
}
|
|
2903
2392
|
};
|
|
2904
2393
|
const executionPrompt = this.buildExecutionPrompt(contextualPrompt);
|
|
2905
|
-
const agentTaskType = this.inferAgentTaskType(
|
|
2394
|
+
const agentTaskType = this.inferAgentTaskType(prompt);
|
|
2906
2395
|
const workspaceContext = {
|
|
2907
2396
|
workspacePath: workspacePath,
|
|
2908
2397
|
projectPath: workspacePath,
|
|
2909
2398
|
targetPath: workspacePath,
|
|
2910
2399
|
...runtimeContext,
|
|
2911
2400
|
};
|
|
2912
|
-
if (!this.jsonOutput && !this.directPromptMode) {
|
|
2913
|
-
try {
|
|
2914
|
-
const snapshot = this.api.getAgentWorkspaceSnapshot(workspacePath);
|
|
2915
|
-
console.log(chalk.gray(`Workspace sync: ${snapshot.fileCount} files indexed from ${workspacePath}`));
|
|
2916
|
-
if (snapshot.paths.length > 0) {
|
|
2917
|
-
console.log(chalk.gray(`Workspace index: ${snapshot.paths.slice(0, 5).map((filePath) => filePath.replace(/\\/g, '/')).join(', ')}${snapshot.paths.length > 5 ? ', ...' : ''}`));
|
|
2918
|
-
}
|
|
2919
|
-
}
|
|
2920
|
-
catch {
|
|
2921
|
-
console.log(chalk.gray(`Workspace sync: preparing ${workspacePath}`));
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
2401
|
// Start workspace watcher for bidirectional real-time sync
|
|
2925
2402
|
let watcher = null;
|
|
2926
|
-
let workspaceWs = null;
|
|
2927
|
-
const stopWorkspaceSync = () => {
|
|
2928
|
-
watcher?.stop();
|
|
2929
|
-
if (workspaceWs) {
|
|
2930
|
-
workspaceWs.disconnect();
|
|
2931
|
-
workspaceWs = null;
|
|
2932
|
-
}
|
|
2933
|
-
};
|
|
2934
2403
|
if (this.shouldStartWorkspaceWatcher(workspacePath)) {
|
|
2935
|
-
watcher = new WorkspaceWatcher({
|
|
2404
|
+
watcher = new workspace_stream_js_1.WorkspaceWatcher({
|
|
2936
2405
|
workspaceRoot: workspacePath,
|
|
2937
2406
|
onFileChange: (relativePath, content, action) => {
|
|
2938
2407
|
this.logger.debug(`Local change detected: ${action} ${relativePath}`);
|
|
2939
|
-
workspaceWs?.syncFile(relativePath, content, action);
|
|
2940
2408
|
},
|
|
2941
2409
|
});
|
|
2942
2410
|
watcher.start();
|
|
@@ -2946,15 +2414,15 @@ export class ChatCommand {
|
|
|
2946
2414
|
await this.api.ensureV3ServiceKey();
|
|
2947
2415
|
if (spinner)
|
|
2948
2416
|
this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Checking V3 connection (preflight)...' });
|
|
2949
|
-
|
|
2417
|
+
// Early health check: fail fast if V3 is not available
|
|
2418
|
+
const healthCheck = await this.api.runV3HealthCheck();
|
|
2950
2419
|
if (!healthCheck.healthy) {
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2420
|
+
const errorMsg = healthCheck.error || 'V3 agent service is not responding. The service may be down or unreachable.';
|
|
2421
|
+
this.logger.error('V3 agent health check failed: ' + errorMsg);
|
|
2422
|
+
throw new Error(errorMsg);
|
|
2954
2423
|
}
|
|
2955
|
-
|
|
2424
|
+
if (spinner)
|
|
2956
2425
|
this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Connected to V3. Starting agent execution...' });
|
|
2957
|
-
}
|
|
2958
2426
|
const workflowPromise = this.api.runV3AgentWorkflow(executionPrompt, {
|
|
2959
2427
|
workspace: { path: workspacePath },
|
|
2960
2428
|
...workspaceContext,
|
|
@@ -2972,40 +2440,23 @@ export class ChatCommand {
|
|
|
2972
2440
|
rawPrompt: prompt,
|
|
2973
2441
|
contextualPrompt,
|
|
2974
2442
|
history: this.getMessagesForModel(),
|
|
2975
|
-
clientToolExecution: true,
|
|
2976
|
-
liveToolEvidence: this.v3LiveToolEvidence,
|
|
2977
|
-
onClientToolExecute: (event) => this.executeClientV3Tool(event),
|
|
2978
|
-
onWorkspaceContext: async ({ contextId, serverWorkspaceRoot }) => {
|
|
2979
|
-
if (workspaceWs || !contextId || !serverWorkspaceRoot || !this.shouldStartWorkspaceWatcher(workspacePath)) {
|
|
2980
|
-
return;
|
|
2981
|
-
}
|
|
2982
|
-
const headers = await this.api.getV3AgentHeaders();
|
|
2983
|
-
const token = String(headers.Authorization?.replace(/^Bearer\s+/i, '')
|
|
2984
|
-
|| headers['X-V3-Service-Key']
|
|
2985
|
-
|| headers['x-v3-service-key']
|
|
2986
|
-
|| '').trim();
|
|
2987
|
-
if (!token) {
|
|
2988
|
-
return;
|
|
2989
|
-
}
|
|
2990
|
-
const baseUrl = this.api.getV3AgentBaseUrls(false)[0] || 'https://coder.vigthoria.io';
|
|
2991
|
-
const serverUrl = baseUrl.replace(/^http/i, 'ws');
|
|
2992
|
-
workspaceWs = new WorkspaceWSClient({
|
|
2993
|
-
serverUrl,
|
|
2994
|
-
token,
|
|
2995
|
-
contextId,
|
|
2996
|
-
workspaceRoot: serverWorkspaceRoot,
|
|
2997
|
-
});
|
|
2998
|
-
workspaceWs.connect();
|
|
2999
|
-
this.logger.debug(`Workspace WS sync bound to context ${contextId}`);
|
|
3000
|
-
},
|
|
3001
2443
|
...runtimeContext,
|
|
3002
2444
|
onStreamEvent: (event) => {
|
|
3003
2445
|
if (event.type === 'plan') {
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
2446
|
+
const plan = event?.plan || {};
|
|
2447
|
+
const isPlanningKeepalive = plan.status === 'planning' && !Array.isArray(plan.tasks);
|
|
2448
|
+
if (isPlanningKeepalive) {
|
|
2449
|
+
const elapsed = Number.isFinite(Number(plan.elapsed_seconds)) ? Number(plan.elapsed_seconds) : 0;
|
|
2450
|
+
taskDisplay.start(0, elapsed > 0 ? `planning (${elapsed}s)` : 'planning...');
|
|
2451
|
+
}
|
|
2452
|
+
else {
|
|
2453
|
+
taskDisplay.complete(0);
|
|
2454
|
+
const tasks = plan?.tasks;
|
|
2455
|
+
if (Array.isArray(tasks) && tasks.length > 0) {
|
|
2456
|
+
taskDisplay.start(1);
|
|
2457
|
+
liveOutcome.tasksTotal = tasks.length;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
3009
2460
|
}
|
|
3010
2461
|
else if (event.type === 'executor_start') {
|
|
3011
2462
|
taskDisplay.start(1);
|
|
@@ -3057,34 +2508,6 @@ export class ChatCommand {
|
|
|
3057
2508
|
if (this.v3StreamingStarted) {
|
|
3058
2509
|
process.stdout.write('\n');
|
|
3059
2510
|
}
|
|
3060
|
-
let finalContent = String(response.content || '').trim();
|
|
3061
|
-
const needsUserReport = this.inferAgentTaskType(prompt) === 'analysis'
|
|
3062
|
-
|| this.isThinV3Summary(finalContent)
|
|
3063
|
-
|| this.v3LiveToolEvidence.length > 0;
|
|
3064
|
-
if (needsUserReport) {
|
|
3065
|
-
const userReport = this.buildUserFacingV3RunReport(prompt, workspacePath, {
|
|
3066
|
-
partial: response.partial === true || this.isThinV3Summary(finalContent),
|
|
3067
|
-
serverNote: finalContent && !this.isThinV3Summary(finalContent) ? finalContent : undefined,
|
|
3068
|
-
});
|
|
3069
|
-
if (userReport && !this.isThinV3Summary(userReport)) {
|
|
3070
|
-
finalContent = userReport;
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
else if (this.isThinV3Summary(finalContent) && this.v3LiveToolEvidence.length > 0) {
|
|
3074
|
-
const evidenceSummary = this.api.formatV3AgentResponse({
|
|
3075
|
-
events: [],
|
|
3076
|
-
liveToolEvidence: this.v3LiveToolEvidence,
|
|
3077
|
-
});
|
|
3078
|
-
if (evidenceSummary && !this.isThinV3Summary(evidenceSummary)) {
|
|
3079
|
-
finalContent = evidenceSummary;
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
response.content = finalContent || response.content;
|
|
3083
|
-
let v3UserReportPrinted = false;
|
|
3084
|
-
if (!this.jsonOutput && finalContent && needsUserReport) {
|
|
3085
|
-
this.printV3UserReport(finalContent);
|
|
3086
|
-
v3UserReportPrinted = true;
|
|
3087
|
-
}
|
|
3088
2511
|
const previewGate = (response.metadata?.previewGate || null);
|
|
3089
2512
|
const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
|
|
3090
2513
|
const success = previewGate?.required === true
|
|
@@ -3097,7 +2520,7 @@ export class ChatCommand {
|
|
|
3097
2520
|
}
|
|
3098
2521
|
this.logger.warn('Falling back to legacy CLI agent loop');
|
|
3099
2522
|
this.logger.debug(`V3 agent workflow returned an incomplete result: ${previewGate?.error || 'workspace changes were not fully validated'}`);
|
|
3100
|
-
|
|
2523
|
+
watcher?.stop();
|
|
3101
2524
|
return false;
|
|
3102
2525
|
}
|
|
3103
2526
|
const errorMessage = `V3 agent workflow returned an incomplete result and legacy fallback is disabled. ${previewGate?.error || 'Workspace changes were not fully validated.'}`;
|
|
@@ -3118,11 +2541,11 @@ export class ChatCommand {
|
|
|
3118
2541
|
metadata: { executionPath: 'v3-agent', previewGate },
|
|
3119
2542
|
}, null, 2));
|
|
3120
2543
|
}
|
|
3121
|
-
|
|
2544
|
+
watcher?.stop();
|
|
3122
2545
|
return true;
|
|
3123
2546
|
}
|
|
3124
2547
|
if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
|
|
3125
|
-
console.log(
|
|
2548
|
+
console.log(chalk_1.default.yellow(`Template Service preview gate did not fully validate this output, but generated workspace files were preserved${previewGate?.error ? `: ${previewGate.error}` : '.'}`));
|
|
3126
2549
|
}
|
|
3127
2550
|
if (this.jsonOutput) {
|
|
3128
2551
|
console.log(JSON.stringify({
|
|
@@ -3138,59 +2561,43 @@ export class ChatCommand {
|
|
|
3138
2561
|
}, null, 2));
|
|
3139
2562
|
}
|
|
3140
2563
|
else if (this.v3StreamingStarted) {
|
|
3141
|
-
// Content
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
|
|
3145
|
-
}
|
|
3146
|
-
if (!v3UserReportPrinted && response.content && this.shouldPrintV3FinalContent(response.content)) {
|
|
3147
|
-
console.log(response.content);
|
|
3148
|
-
}
|
|
3149
|
-
else if (!v3UserReportPrinted && response.content && this.isThinV3Summary(response.content)) {
|
|
3150
|
-
console.log(chalk.yellow('V3 agent finished after reading local files, but no final narrative summary was emitted.'));
|
|
3151
|
-
console.log(chalk.gray('Use /continue and ask for a written overview.'));
|
|
2564
|
+
// Content was already streamed to stdout in real-time; skip duplicate print.
|
|
2565
|
+
if (!this.jsonOutput) {
|
|
2566
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
|
|
3152
2567
|
}
|
|
3153
2568
|
}
|
|
3154
2569
|
else if (response.content) {
|
|
3155
|
-
if (!
|
|
3156
|
-
console.log(
|
|
3157
|
-
}
|
|
3158
|
-
if (!v3UserReportPrinted && this.shouldPrintV3FinalContent(response.content)) {
|
|
3159
|
-
console.log(response.content);
|
|
3160
|
-
}
|
|
3161
|
-
else if (!v3UserReportPrinted && this.isThinV3Summary(response.content)) {
|
|
3162
|
-
console.log('V3 agent workflow completed, but no final task summary was emitted.');
|
|
3163
|
-
}
|
|
3164
|
-
else if (!v3UserReportPrinted) {
|
|
3165
|
-
console.log(response.content);
|
|
2570
|
+
if (!this.directPromptMode) {
|
|
2571
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
|
|
3166
2572
|
}
|
|
2573
|
+
console.log(response.content);
|
|
3167
2574
|
}
|
|
3168
2575
|
else {
|
|
3169
2576
|
if (!this.directPromptMode) {
|
|
3170
|
-
console.log(
|
|
2577
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
|
|
3171
2578
|
}
|
|
3172
2579
|
console.log('V3 agent workflow completed.');
|
|
3173
2580
|
}
|
|
3174
2581
|
if (!this.jsonOutput && previewGate?.required) {
|
|
3175
2582
|
if (previewGate.passed) {
|
|
3176
|
-
console.log(
|
|
2583
|
+
console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
|
|
3177
2584
|
}
|
|
3178
2585
|
else {
|
|
3179
|
-
console.log(
|
|
2586
|
+
console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
|
|
3180
2587
|
}
|
|
3181
2588
|
}
|
|
3182
2589
|
// Show change summary for files touched by the agent
|
|
3183
2590
|
if (!this.jsonOutput && !this.directPromptMode && response.changedFiles) {
|
|
3184
2591
|
const fileCount = Object.keys(response.changedFiles).length;
|
|
3185
2592
|
if (fileCount > 0) {
|
|
3186
|
-
console.log(
|
|
2593
|
+
console.log(chalk_1.default.gray(`\nFiles changed: ${fileCount}`));
|
|
3187
2594
|
for (const relPath of Object.keys(response.changedFiles).slice(0, 15)) {
|
|
3188
|
-
console.log(
|
|
2595
|
+
console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
|
|
3189
2596
|
}
|
|
3190
2597
|
if (fileCount > 15) {
|
|
3191
|
-
console.log(
|
|
2598
|
+
console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
|
|
3192
2599
|
}
|
|
3193
|
-
console.log(
|
|
2600
|
+
console.log(chalk_1.default.gray(`Run ${chalk_1.default.cyan('vigthoria preview --diff')} for full visual diffs.`));
|
|
3194
2601
|
}
|
|
3195
2602
|
}
|
|
3196
2603
|
// Resolve the Execute-tasks spinner: only mark it ✓ when the run actually
|
|
@@ -3228,8 +2635,8 @@ export class ChatCommand {
|
|
|
3228
2635
|
}
|
|
3229
2636
|
selfHealTool = healResult.tool || null;
|
|
3230
2637
|
if (!this.directPromptMode) {
|
|
3231
|
-
const hs = healResult.passed ?
|
|
3232
|
-
console.log(
|
|
2638
|
+
const hs = healResult.passed ? chalk_1.default.green('passed') : chalk_1.default.yellow('partial');
|
|
2639
|
+
console.log(chalk_1.default.gray(`Self-healing: ${hs} (${healResult.tool})`));
|
|
3233
2640
|
}
|
|
3234
2641
|
}
|
|
3235
2642
|
else {
|
|
@@ -3266,8 +2673,8 @@ export class ChatCommand {
|
|
|
3266
2673
|
hasOutput: workspaceHasOutput,
|
|
3267
2674
|
selfHealStatus,
|
|
3268
2675
|
selfHealTool,
|
|
3269
|
-
plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
|
|
3270
|
-
executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : null,
|
|
2676
|
+
plannerError: liveOutcome.plannerError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.plannerError) : null,
|
|
2677
|
+
executorError: liveOutcome.executorError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.executorError) : null,
|
|
3271
2678
|
finishedAt: Date.now(),
|
|
3272
2679
|
};
|
|
3273
2680
|
if (!this.jsonOutput && !this.directPromptMode) {
|
|
@@ -3275,11 +2682,11 @@ export class ChatCommand {
|
|
|
3275
2682
|
this.printAgentRunSummary(this.lastAgentRunOutcome, executorSucceeded, changedFileCount);
|
|
3276
2683
|
}
|
|
3277
2684
|
this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
|
|
3278
|
-
|
|
2685
|
+
watcher?.stop();
|
|
3279
2686
|
return true;
|
|
3280
2687
|
}
|
|
3281
2688
|
catch (error) {
|
|
3282
|
-
|
|
2689
|
+
watcher?.stop();
|
|
3283
2690
|
if (!this.api.hasAgentWorkspaceOutput(workspaceContext)) {
|
|
3284
2691
|
const recovered = await this.tryRecoverV3ServiceAndRetry(executionPrompt, prompt, workspaceContext, routingPolicy, spinner, error);
|
|
3285
2692
|
if (recovered) {
|
|
@@ -3291,13 +2698,13 @@ export class ChatCommand {
|
|
|
3291
2698
|
spinner.stop();
|
|
3292
2699
|
}
|
|
3293
2700
|
this.logger.warn('Falling back to legacy CLI agent loop');
|
|
3294
|
-
this.logger.debug(`V3 agent workflow unavailable: ${sanitizeUserFacingErrorText(error.message || '')}`);
|
|
2701
|
+
this.logger.debug(`V3 agent workflow unavailable: ${(0, api_js_1.sanitizeUserFacingErrorText)(error.message || '')}`);
|
|
3295
2702
|
return false;
|
|
3296
2703
|
}
|
|
3297
2704
|
if (spinner) {
|
|
3298
2705
|
spinner.stop();
|
|
3299
2706
|
}
|
|
3300
|
-
const safeDetail = sanitizeUserFacingErrorText(error.message || '');
|
|
2707
|
+
const safeDetail = (0, api_js_1.sanitizeUserFacingErrorText)(error.message || '');
|
|
3301
2708
|
const errorMessage = safeDetail
|
|
3302
2709
|
? `Agent mode is unavailable right now. ${safeDetail}`
|
|
3303
2710
|
: 'Agent mode is unavailable right now. Please retry shortly or run vigthoria login if the issue persists.';
|
|
@@ -3326,8 +2733,8 @@ export class ChatCommand {
|
|
|
3326
2733
|
hasOutput: this.api.hasAgentWorkspaceOutput(workspaceContext),
|
|
3327
2734
|
selfHealStatus: 'skipped',
|
|
3328
2735
|
selfHealTool: null,
|
|
3329
|
-
plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
|
|
3330
|
-
executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : safeDetail || null,
|
|
2736
|
+
plannerError: liveOutcome.plannerError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.plannerError) : null,
|
|
2737
|
+
executorError: liveOutcome.executorError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.executorError) : safeDetail || null,
|
|
3331
2738
|
finishedAt: Date.now(),
|
|
3332
2739
|
};
|
|
3333
2740
|
if (this.jsonOutput) {
|
|
@@ -3357,7 +2764,7 @@ export class ChatCommand {
|
|
|
3357
2764
|
spinner.stop();
|
|
3358
2765
|
}
|
|
3359
2766
|
if (!this.jsonOutput) {
|
|
3360
|
-
console.log(
|
|
2767
|
+
console.log(chalk_1.default.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
|
|
3361
2768
|
}
|
|
3362
2769
|
this.v3IterationCount = 0;
|
|
3363
2770
|
this.v3ToolCallCount = 0;
|
|
@@ -3401,7 +2808,7 @@ export class ChatCommand {
|
|
|
3401
2808
|
}, null, 2));
|
|
3402
2809
|
}
|
|
3403
2810
|
else if (!this.v3StreamingStarted && retryResponse.content) {
|
|
3404
|
-
console.log(
|
|
2811
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
|
|
3405
2812
|
console.log(retryResponse.content);
|
|
3406
2813
|
}
|
|
3407
2814
|
this.messages.push({ role: 'assistant', content: retryResponse.content || 'V3 agent workflow completed after recovery.' });
|
|
@@ -3418,10 +2825,10 @@ export class ChatCommand {
|
|
|
3418
2825
|
? 'Interactive Agent Chat'
|
|
3419
2826
|
: 'Interactive Chat';
|
|
3420
2827
|
this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
|
|
3421
|
-
console.log(
|
|
3422
|
-
console.log(
|
|
2828
|
+
console.log(chalk_1.default.gray('Type /help for commands. Type /exit to quit.'));
|
|
2829
|
+
console.log(chalk_1.default.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
|
|
3423
2830
|
if (this.workflowTarget) {
|
|
3424
|
-
console.log(
|
|
2831
|
+
console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
|
|
3425
2832
|
}
|
|
3426
2833
|
const rl = readline.createInterface({
|
|
3427
2834
|
input: process.stdin,
|
|
@@ -3431,17 +2838,17 @@ export class ChatCommand {
|
|
|
3431
2838
|
const readMultiLineInput = async () => {
|
|
3432
2839
|
const lines = [];
|
|
3433
2840
|
let firstLine = await new Promise((resolve) => {
|
|
3434
|
-
rl.question(
|
|
2841
|
+
rl.question(chalk_1.default.blue('> '), resolve);
|
|
3435
2842
|
});
|
|
3436
2843
|
// Check for {{{ block mode
|
|
3437
2844
|
if (firstLine.trim() === '{{{' || firstLine.trim().endsWith('{{{')) {
|
|
3438
2845
|
if (firstLine.trim() !== '{{{') {
|
|
3439
2846
|
lines.push(firstLine.trim().replace(/\{\{\{$/, '').trim());
|
|
3440
2847
|
}
|
|
3441
|
-
console.log(
|
|
2848
|
+
console.log(chalk_1.default.gray(' (multi-line mode: type }}} on its own line to finish)'));
|
|
3442
2849
|
while (true) {
|
|
3443
2850
|
const line = await new Promise((resolve) => {
|
|
3444
|
-
rl.question(
|
|
2851
|
+
rl.question(chalk_1.default.gray(' '), resolve);
|
|
3445
2852
|
});
|
|
3446
2853
|
if (line.trim() === '}}}')
|
|
3447
2854
|
break;
|
|
@@ -3453,7 +2860,7 @@ export class ChatCommand {
|
|
|
3453
2860
|
while (firstLine.endsWith('\\')) {
|
|
3454
2861
|
lines.push(firstLine.slice(0, -1));
|
|
3455
2862
|
firstLine = await new Promise((resolve) => {
|
|
3456
|
-
rl.question(
|
|
2863
|
+
rl.question(chalk_1.default.gray(' '), resolve);
|
|
3457
2864
|
});
|
|
3458
2865
|
}
|
|
3459
2866
|
lines.push(firstLine);
|
|
@@ -3494,8 +2901,8 @@ export class ChatCommand {
|
|
|
3494
2901
|
else {
|
|
3495
2902
|
this.syncInteractiveModeModel('chat');
|
|
3496
2903
|
}
|
|
3497
|
-
console.log(
|
|
3498
|
-
console.log(
|
|
2904
|
+
console.log(chalk_1.default.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
|
|
2905
|
+
console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
|
|
3499
2906
|
if (this.currentSession) {
|
|
3500
2907
|
this.currentSession.agentMode = this.agentMode;
|
|
3501
2908
|
this.currentSession.operatorMode = this.operatorMode;
|
|
@@ -3517,8 +2924,8 @@ export class ChatCommand {
|
|
|
3517
2924
|
else {
|
|
3518
2925
|
this.syncInteractiveModeModel('chat');
|
|
3519
2926
|
}
|
|
3520
|
-
console.log(
|
|
3521
|
-
console.log(
|
|
2927
|
+
console.log(chalk_1.default.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
|
|
2928
|
+
console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
|
|
3522
2929
|
if (this.currentSession) {
|
|
3523
2930
|
this.currentSession.agentMode = this.agentMode;
|
|
3524
2931
|
this.currentSession.operatorMode = this.operatorMode;
|
|
@@ -3529,7 +2936,7 @@ export class ChatCommand {
|
|
|
3529
2936
|
}
|
|
3530
2937
|
if (trimmed === '/clear') {
|
|
3531
2938
|
this.messages = [];
|
|
3532
|
-
console.log(
|
|
2939
|
+
console.log(chalk_1.default.yellow('Conversation cleared.'));
|
|
3533
2940
|
continue;
|
|
3534
2941
|
}
|
|
3535
2942
|
if (trimmed === '/status') {
|
|
@@ -3539,7 +2946,7 @@ export class ChatCommand {
|
|
|
3539
2946
|
if (trimmed === '/retry') {
|
|
3540
2947
|
const followUp = this.buildRetryPrompt();
|
|
3541
2948
|
if (!followUp) {
|
|
3542
|
-
console.log(
|
|
2949
|
+
console.log(chalk_1.default.yellow('Nothing to retry — run an agent task first.'));
|
|
3543
2950
|
continue;
|
|
3544
2951
|
}
|
|
3545
2952
|
const nextSignature = this.computeRetryPromptSignature();
|
|
@@ -3553,11 +2960,11 @@ export class ChatCommand {
|
|
|
3553
2960
|
if (this.retryPromptStreak >= 3) {
|
|
3554
2961
|
const continuePrompt = this.buildContinuePrompt();
|
|
3555
2962
|
if (continuePrompt) {
|
|
3556
|
-
console.log(
|
|
2963
|
+
console.log(chalk_1.default.yellow('Repeated /retry detected with the same failing task set. Escalating to /continue to avoid planner loops.'));
|
|
3557
2964
|
if (!this.agentMode) {
|
|
3558
2965
|
this.agentMode = true;
|
|
3559
2966
|
this.syncInteractiveModeModel('agent');
|
|
3560
|
-
console.log(
|
|
2967
|
+
console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
|
|
3561
2968
|
}
|
|
3562
2969
|
await this.runAgentTurn(continuePrompt);
|
|
3563
2970
|
continue;
|
|
@@ -3566,7 +2973,7 @@ export class ChatCommand {
|
|
|
3566
2973
|
if (!this.agentMode) {
|
|
3567
2974
|
this.agentMode = true;
|
|
3568
2975
|
this.syncInteractiveModeModel('agent');
|
|
3569
|
-
console.log(
|
|
2976
|
+
console.log(chalk_1.default.gray('Agent mode re-enabled for retry.'));
|
|
3570
2977
|
}
|
|
3571
2978
|
await this.runAgentTurn(followUp);
|
|
3572
2979
|
continue;
|
|
@@ -3574,32 +2981,26 @@ export class ChatCommand {
|
|
|
3574
2981
|
if (trimmed === '/continue') {
|
|
3575
2982
|
const followUp = this.buildContinuePrompt();
|
|
3576
2983
|
if (!followUp) {
|
|
3577
|
-
console.log(
|
|
2984
|
+
console.log(chalk_1.default.yellow('Nothing to continue — run an agent task first.'));
|
|
3578
2985
|
continue;
|
|
3579
2986
|
}
|
|
3580
2987
|
if (!this.agentMode) {
|
|
3581
2988
|
this.agentMode = true;
|
|
3582
2989
|
this.syncInteractiveModeModel('agent');
|
|
3583
|
-
console.log(
|
|
2990
|
+
console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
|
|
3584
2991
|
}
|
|
3585
|
-
await this.runAgentTurn(followUp
|
|
2992
|
+
await this.runAgentTurn(followUp);
|
|
3586
2993
|
continue;
|
|
3587
2994
|
}
|
|
3588
|
-
if (/^(?:\/logout|logout)$/i.test(trimmed)) {
|
|
3589
|
-
const { logout } = await import('./auth.js');
|
|
3590
|
-
await logout();
|
|
3591
|
-
console.log(chalk.gray('Session ended.'));
|
|
3592
|
-
break;
|
|
3593
|
-
}
|
|
3594
2995
|
if (trimmed === '/save') {
|
|
3595
2996
|
this.saveSession();
|
|
3596
|
-
console.log(
|
|
2997
|
+
console.log(chalk_1.default.green('Session saved.'));
|
|
3597
2998
|
continue;
|
|
3598
2999
|
}
|
|
3599
3000
|
if (trimmed.startsWith('/model ')) {
|
|
3600
3001
|
this.currentModel = trimmed.slice(7).trim() || this.currentModel;
|
|
3601
3002
|
this.modelExplicitlySelected = true;
|
|
3602
|
-
console.log(
|
|
3003
|
+
console.log(chalk_1.default.yellow(`Model changed to: ${this.currentModel}`));
|
|
3603
3004
|
if (this.currentSession) {
|
|
3604
3005
|
this.currentSession.model = this.currentModel;
|
|
3605
3006
|
this.saveSession();
|
|
@@ -3647,74 +3048,71 @@ export class ChatCommand {
|
|
|
3647
3048
|
* - Never leave the spinners (`⟳`, `–`) ambiguous when the prompt returns.
|
|
3648
3049
|
*/
|
|
3649
3050
|
printAgentRunSummary(outcome, executorSucceeded, changedFileCount) {
|
|
3650
|
-
const bar =
|
|
3651
|
-
const ok =
|
|
3652
|
-
const warn =
|
|
3653
|
-
const bad =
|
|
3051
|
+
const bar = chalk_1.default.gray('─'.repeat(63));
|
|
3052
|
+
const ok = chalk_1.default.green('✓');
|
|
3053
|
+
const warn = chalk_1.default.yellow('⚠');
|
|
3054
|
+
const bad = chalk_1.default.red('✗');
|
|
3654
3055
|
const failedList = outcome.failedTaskIds.slice(0, 8);
|
|
3655
3056
|
const unfinishedList = outcome.unfinishedTaskIds.slice(0, 8);
|
|
3656
3057
|
const hasTaskInfo = outcome.tasksTotal > 0 || failedList.length > 0 || unfinishedList.length > 0;
|
|
3657
3058
|
console.log('');
|
|
3658
3059
|
console.log(bar);
|
|
3659
3060
|
if (executorSucceeded && outcome.selfHealStatus !== 'partial' && outcome.selfHealStatus !== 'failed' && failedList.length === 0) {
|
|
3660
|
-
console.log(`${ok} ${
|
|
3061
|
+
console.log(`${ok} ${chalk_1.default.bold('Agent run finished')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
|
|
3661
3062
|
}
|
|
3662
3063
|
else if (executorSucceeded) {
|
|
3663
|
-
console.log(`${warn} ${
|
|
3064
|
+
console.log(`${warn} ${chalk_1.default.bold('Agent run finished with warnings')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
|
|
3664
3065
|
}
|
|
3665
3066
|
else {
|
|
3666
|
-
console.log(`${bad} ${
|
|
3067
|
+
console.log(`${bad} ${chalk_1.default.bold('Agent run did not complete')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
|
|
3667
3068
|
}
|
|
3668
3069
|
if (hasTaskInfo) {
|
|
3669
3070
|
const succ = outcome.tasksTotal > 0 ? `${outcome.tasksSucceeded}/${outcome.tasksTotal}` : `${outcome.tasksSucceeded}`;
|
|
3670
|
-
console.log(
|
|
3071
|
+
console.log(chalk_1.default.gray(` Tasks completed: ${succ}`));
|
|
3671
3072
|
if (failedList.length > 0) {
|
|
3672
|
-
const more = outcome.failedTaskIds.length > failedList.length ?
|
|
3673
|
-
console.log(
|
|
3073
|
+
const more = outcome.failedTaskIds.length > failedList.length ? chalk_1.default.gray(` (+${outcome.failedTaskIds.length - failedList.length} more)`) : '';
|
|
3074
|
+
console.log(chalk_1.default.gray(' Failed: ') + chalk_1.default.red(failedList.join(', ')) + more);
|
|
3674
3075
|
}
|
|
3675
3076
|
if (unfinishedList.length > 0) {
|
|
3676
|
-
const more = outcome.unfinishedTaskIds.length > unfinishedList.length ?
|
|
3677
|
-
console.log(
|
|
3077
|
+
const more = outcome.unfinishedTaskIds.length > unfinishedList.length ? chalk_1.default.gray(` (+${outcome.unfinishedTaskIds.length - unfinishedList.length} more)`) : '';
|
|
3078
|
+
console.log(chalk_1.default.gray(' Pending: ') + chalk_1.default.yellow(unfinishedList.join(', ')) + more);
|
|
3678
3079
|
}
|
|
3679
3080
|
}
|
|
3680
|
-
else if (!executorSucceeded) {
|
|
3681
|
-
console.log(chalk.gray(' The detailed overview is printed above when available.'));
|
|
3682
|
-
}
|
|
3683
3081
|
if (typeof outcome.qualityScore === 'number') {
|
|
3684
3082
|
const score = outcome.qualityScore.toFixed(1);
|
|
3685
|
-
const colour = outcome.qualityScore >= 70 ?
|
|
3686
|
-
console.log(
|
|
3083
|
+
const colour = outcome.qualityScore >= 70 ? chalk_1.default.green : outcome.qualityScore >= 30 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
3084
|
+
console.log(chalk_1.default.gray(' Quality: ') + colour(`${score}/100`));
|
|
3687
3085
|
}
|
|
3688
3086
|
if (outcome.qualityBlockers.length > 0) {
|
|
3689
3087
|
const list = outcome.qualityBlockers.slice(0, 3).join('; ');
|
|
3690
|
-
const more = outcome.qualityBlockers.length > 3 ?
|
|
3691
|
-
console.log(
|
|
3088
|
+
const more = outcome.qualityBlockers.length > 3 ? chalk_1.default.gray(` (+${outcome.qualityBlockers.length - 3} more)`) : '';
|
|
3089
|
+
console.log(chalk_1.default.gray(' Blockers: ') + chalk_1.default.yellow(list) + more);
|
|
3692
3090
|
}
|
|
3693
3091
|
if (outcome.qualityMissing.length > 0) {
|
|
3694
3092
|
const list = outcome.qualityMissing.slice(0, 5).join(', ');
|
|
3695
|
-
const more = outcome.qualityMissing.length > 5 ?
|
|
3696
|
-
console.log(
|
|
3093
|
+
const more = outcome.qualityMissing.length > 5 ? chalk_1.default.gray(` (+${outcome.qualityMissing.length - 5} more)`) : '';
|
|
3094
|
+
console.log(chalk_1.default.gray(' Missing: ') + chalk_1.default.yellow(list) + more);
|
|
3697
3095
|
}
|
|
3698
3096
|
if (outcome.plannerError) {
|
|
3699
|
-
console.log(
|
|
3097
|
+
console.log(chalk_1.default.gray(' Planner: ') + chalk_1.default.red(outcome.plannerError.slice(0, 140)));
|
|
3700
3098
|
}
|
|
3701
3099
|
if (outcome.executorError) {
|
|
3702
|
-
console.log(
|
|
3100
|
+
console.log(chalk_1.default.gray(' Executor: ') + chalk_1.default.red(outcome.executorError.slice(0, 140)));
|
|
3703
3101
|
}
|
|
3704
3102
|
console.log('');
|
|
3705
|
-
console.log(
|
|
3103
|
+
console.log(chalk_1.default.gray('What you can do next:'));
|
|
3706
3104
|
if (failedList.length > 0 || unfinishedList.length > 0 || !executorSucceeded) {
|
|
3707
|
-
console.log(' ' +
|
|
3708
|
-
console.log(' ' +
|
|
3105
|
+
console.log(' ' + chalk_1.default.cyan('/retry') + chalk_1.default.gray(' resume the failed/unfinished tasks only'));
|
|
3106
|
+
console.log(' ' + chalk_1.default.cyan('/continue') + chalk_1.default.gray(' ask the agent to keep working from this state'));
|
|
3709
3107
|
}
|
|
3710
3108
|
else {
|
|
3711
|
-
console.log(' ' +
|
|
3109
|
+
console.log(' ' + chalk_1.default.cyan('/continue') + chalk_1.default.gray(' add a follow-up instruction in this thread'));
|
|
3712
3110
|
}
|
|
3713
3111
|
if (changedFileCount > 0) {
|
|
3714
|
-
console.log(' ' +
|
|
3112
|
+
console.log(' ' + chalk_1.default.cyan('vigthoria preview --diff') + chalk_1.default.gray(' inspect the file changes'));
|
|
3715
3113
|
}
|
|
3716
|
-
console.log(' ' +
|
|
3717
|
-
console.log(' ' +
|
|
3114
|
+
console.log(' ' + chalk_1.default.cyan('/status') + chalk_1.default.gray(' re-print this summary later'));
|
|
3115
|
+
console.log(' ' + chalk_1.default.cyan('/exit') + chalk_1.default.gray(' leave interactive chat'));
|
|
3718
3116
|
console.log(bar);
|
|
3719
3117
|
console.log('');
|
|
3720
3118
|
}
|
|
@@ -3768,18 +3166,15 @@ export class ChatCommand {
|
|
|
3768
3166
|
const missingLine = o.qualityMissing.length > 0
|
|
3769
3167
|
? `\nMissing pieces: ${o.qualityMissing.slice(0, 6).join(', ')}.`
|
|
3770
3168
|
: '';
|
|
3771
|
-
return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${blockerLine}${missingLine}
|
|
3772
|
-
Original request was: ${o.prompt}
|
|
3773
|
-
|
|
3774
|
-
Now implement the missing fixes by editing the local workspace files. Use write/edit tools to apply the required code and HTML changes so the project runs.`;
|
|
3169
|
+
return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${blockerLine}${missingLine}\nOriginal request was: ${o.prompt}`;
|
|
3775
3170
|
}
|
|
3776
3171
|
/**
|
|
3777
3172
|
* Re-print the last agent run summary, or guide the user when there isn't one.
|
|
3778
3173
|
*/
|
|
3779
3174
|
showAgentRunStatus() {
|
|
3780
3175
|
if (!this.lastAgentRunOutcome) {
|
|
3781
|
-
console.log(
|
|
3782
|
-
console.log(
|
|
3176
|
+
console.log(chalk_1.default.yellow('No agent run has finished in this session yet.'));
|
|
3177
|
+
console.log(chalk_1.default.gray('Toggle agent mode with /agent and send a request to start one.'));
|
|
3783
3178
|
return;
|
|
3784
3179
|
}
|
|
3785
3180
|
const o = this.lastAgentRunOutcome;
|
|
@@ -3792,45 +3187,45 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3792
3187
|
}
|
|
3793
3188
|
showContext() {
|
|
3794
3189
|
if (!this.currentSession) {
|
|
3795
|
-
console.log(
|
|
3190
|
+
console.log(chalk_1.default.yellow('No active session.'));
|
|
3796
3191
|
return;
|
|
3797
3192
|
}
|
|
3798
|
-
console.log(
|
|
3193
|
+
console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
|
|
3799
3194
|
if (this.currentSession.memorySummary?.trim()) {
|
|
3800
3195
|
console.log();
|
|
3801
|
-
console.log(
|
|
3802
|
-
console.log(
|
|
3196
|
+
console.log(chalk_1.default.white('Compact Session Memory:'));
|
|
3197
|
+
console.log(chalk_1.default.gray(this.currentSession.memorySummary.trim()));
|
|
3803
3198
|
}
|
|
3804
3199
|
else {
|
|
3805
|
-
console.log(
|
|
3200
|
+
console.log(chalk_1.default.gray('No compact session memory summary yet.'));
|
|
3806
3201
|
}
|
|
3807
3202
|
const runtime = this.getRuntimeEnvironmentContext();
|
|
3808
3203
|
console.log();
|
|
3809
|
-
console.log(
|
|
3810
|
-
console.log(
|
|
3811
|
-
console.log(
|
|
3812
|
-
console.log(
|
|
3813
|
-
console.log(
|
|
3814
|
-
console.log(
|
|
3204
|
+
console.log(chalk_1.default.white('Runtime Environment:'));
|
|
3205
|
+
console.log(chalk_1.default.gray(`Platform: ${runtime.platform} (${runtime.osPlatform})`));
|
|
3206
|
+
console.log(chalk_1.default.gray(`Machine scope: ${runtime.machineScope}`));
|
|
3207
|
+
console.log(chalk_1.default.gray(`Workspace: ${runtime.workspacePath}`));
|
|
3208
|
+
console.log(chalk_1.default.gray(`CWD: ${runtime.cwd}`));
|
|
3209
|
+
console.log(chalk_1.default.gray(`Server-bindable workspace: ${runtime.serverBindableWorkspace ? 'yes' : 'no'}`));
|
|
3815
3210
|
this.showProjectMemory();
|
|
3816
3211
|
}
|
|
3817
3212
|
showProjectMemory() {
|
|
3818
3213
|
if (!this.projectMemory) {
|
|
3819
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
3214
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
3820
3215
|
}
|
|
3821
3216
|
const status = this.projectMemory.getStatus();
|
|
3822
3217
|
console.log();
|
|
3823
|
-
console.log(
|
|
3824
|
-
console.log(
|
|
3825
|
-
console.log(
|
|
3218
|
+
console.log(chalk_1.default.white('Project Brain:'));
|
|
3219
|
+
console.log(chalk_1.default.gray(`Path: ${status.memoryDir}`));
|
|
3220
|
+
console.log(chalk_1.default.gray(`Items: ${status.itemCount}`));
|
|
3826
3221
|
const typeSummary = Object.entries(status.typeCounts).map(([type, count]) => `${type}=${count}`).join(', ');
|
|
3827
3222
|
if (typeSummary) {
|
|
3828
|
-
console.log(
|
|
3223
|
+
console.log(chalk_1.default.gray(`Types: ${typeSummary}`));
|
|
3829
3224
|
}
|
|
3830
3225
|
}
|
|
3831
3226
|
compactCurrentSession() {
|
|
3832
3227
|
if (!this.currentSession) {
|
|
3833
|
-
console.log(
|
|
3228
|
+
console.log(chalk_1.default.yellow('No active session.'));
|
|
3834
3229
|
return;
|
|
3835
3230
|
}
|
|
3836
3231
|
this.currentSession.messages = [...this.messages];
|
|
@@ -3838,7 +3233,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3838
3233
|
this.messages = [...this.currentSession.messages];
|
|
3839
3234
|
this.sessionManager.save(this.currentSession);
|
|
3840
3235
|
if (!this.projectMemory) {
|
|
3841
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
3236
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
3842
3237
|
}
|
|
3843
3238
|
this.projectMemory.rememberConversation(this.messages, {
|
|
3844
3239
|
source: 'manual-compact',
|
|
@@ -3846,7 +3241,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3846
3241
|
model: this.currentModel,
|
|
3847
3242
|
sessionSummary: this.currentSession.memorySummary || '',
|
|
3848
3243
|
});
|
|
3849
|
-
console.log(
|
|
3244
|
+
console.log(chalk_1.default.green('Session compacted into memory summary and project brain.'));
|
|
3850
3245
|
}
|
|
3851
3246
|
ensureAgentSystemPrompt() {
|
|
3852
3247
|
const hasSystemPrompt = this.messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria CLI agent operating contract'));
|
|
@@ -3859,8 +3254,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3859
3254
|
});
|
|
3860
3255
|
}
|
|
3861
3256
|
buildAgentSystemPrompt() {
|
|
3862
|
-
const
|
|
3863
|
-
const toolCatalog = AgenticTools.getToolDefinitions()
|
|
3257
|
+
const toolCatalog = tools_js_1.AgenticTools.getToolDefinitions()
|
|
3864
3258
|
.map((tool) => {
|
|
3865
3259
|
const params = tool.parameters
|
|
3866
3260
|
.map((param) => `${param.name}${param.required ? ' (required)' : ''}`)
|
|
@@ -3871,7 +3265,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3871
3265
|
return [
|
|
3872
3266
|
'Vigthoria CLI agent operating contract.',
|
|
3873
3267
|
`You are operating inside the project root: ${this.currentProjectPath}`,
|
|
3874
|
-
`Execution platform: ${runtime.platform} (${runtime.osPlatform}). Machine scope: ${runtime.machineScope}.`,
|
|
3875
3268
|
`You are operating on the user's LOCAL machine. This CLI is not a server-only runtime.`,
|
|
3876
3269
|
`All file reads/writes and command execution must target the local project/workspace path unless the user explicitly requests remote/server execution.`,
|
|
3877
3270
|
'For command execution: ask for confirmation before risky commands; never redirect normal local work to server paths.',
|
|
@@ -3904,12 +3297,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3904
3297
|
].join('\n');
|
|
3905
3298
|
}
|
|
3906
3299
|
buildScopedUserPrompt(prompt) {
|
|
3907
|
-
const runtime = this.getRuntimeEnvironmentContext();
|
|
3908
3300
|
return [
|
|
3909
3301
|
this.buildExecutionPrompt(prompt),
|
|
3910
3302
|
'',
|
|
3911
3303
|
`Project root: ${this.currentProjectPath}`,
|
|
3912
|
-
`Execution platform: ${runtime.platform}. Machine scope: ${runtime.machineScope}.`,
|
|
3913
3304
|
'Stay within this project root unless the user explicitly expands scope.',
|
|
3914
3305
|
'Finish the request and stop once it is complete.',
|
|
3915
3306
|
].join('\n');
|
|
@@ -3991,32 +3382,14 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
3991
3382
|
if (this.isBrowserTaskPrompt(prompt)) {
|
|
3992
3383
|
return false;
|
|
3993
3384
|
}
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|| ''));
|
|
3998
|
-
// CLI sessions on a user's local machine must execute tools locally.
|
|
3999
|
-
// Remote V3 only sees a partial hydrated copy and cannot reach real paths.
|
|
4000
|
-
if (runtime.machineScope === 'local-machine') {
|
|
4001
|
-
if (forceRemoteAgent) {
|
|
4002
|
-
return false;
|
|
4003
|
-
}
|
|
4004
|
-
if (this.isImplementationPrompt(prompt)) {
|
|
4005
|
-
return true;
|
|
4006
|
-
}
|
|
4007
|
-
const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
|
|
4008
|
-
// Read-only analysis on a Windows/macOS/Linux desktop is faster and more
|
|
4009
|
-
// reliable through V3 + client-side read tools than a blocking chat route.
|
|
4010
|
-
if (this.isAnalysisLookupPrompt(prompt) && !preferLocalOptIn) {
|
|
4011
|
-
return false;
|
|
4012
|
-
}
|
|
4013
|
-
return true;
|
|
4014
|
-
}
|
|
4015
|
-
// Server-bindable workspaces keep V3 as the default execution path.
|
|
3385
|
+
// Product default: Agent mode routes through V3.
|
|
3386
|
+
// Local loop is opt-in for explicit offline/debug scenarios only.
|
|
3387
|
+
// Explicit local paths now influence project root resolution, not routing mode.
|
|
4016
3388
|
const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
|
|
4017
3389
|
if (!preferLocalOptIn) {
|
|
4018
3390
|
return false;
|
|
4019
3391
|
}
|
|
3392
|
+
const runtime = this.getRuntimeEnvironmentContext();
|
|
4020
3393
|
if (this.directPromptMode && this.isRepoGroundedPrompt(prompt)) {
|
|
4021
3394
|
return false;
|
|
4022
3395
|
}
|
|
@@ -4525,57 +3898,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4525
3898
|
.trim();
|
|
4526
3899
|
return normalized || null;
|
|
4527
3900
|
}
|
|
4528
|
-
async tryLocalToolFallback(call, result) {
|
|
4529
|
-
if (result.success || !this.tools) {
|
|
4530
|
-
return result;
|
|
4531
|
-
}
|
|
4532
|
-
const runtime = this.getRuntimeEnvironmentContext();
|
|
4533
|
-
if (runtime.machineScope !== 'local-machine') {
|
|
4534
|
-
return result;
|
|
4535
|
-
}
|
|
4536
|
-
if (call.tool === 'read_file' && call.args.path) {
|
|
4537
|
-
const parent = path.dirname(call.args.path);
|
|
4538
|
-
const listPath = parent === '.' || parent === '' ? '.' : parent;
|
|
4539
|
-
const listResult = await this.tools.execute({
|
|
4540
|
-
tool: 'list_dir',
|
|
4541
|
-
args: { path: listPath },
|
|
4542
|
-
});
|
|
4543
|
-
if (listResult.success && listResult.output) {
|
|
4544
|
-
return {
|
|
4545
|
-
...result,
|
|
4546
|
-
suggestion: `File not found at "${call.args.path}". Parent directory "${listPath}" contains:\n${listResult.output}`,
|
|
4547
|
-
};
|
|
4548
|
-
}
|
|
4549
|
-
const baseName = path.basename(call.args.path);
|
|
4550
|
-
const globResult = await this.tools.execute({
|
|
4551
|
-
tool: 'glob',
|
|
4552
|
-
args: { pattern: `**/${baseName}` },
|
|
4553
|
-
});
|
|
4554
|
-
if (globResult.success && globResult.output?.trim()) {
|
|
4555
|
-
return {
|
|
4556
|
-
...result,
|
|
4557
|
-
suggestion: `File not found at "${call.args.path}". Matching paths:\n${globResult.output}`,
|
|
4558
|
-
};
|
|
4559
|
-
}
|
|
4560
|
-
}
|
|
4561
|
-
if (call.tool === 'list_dir') {
|
|
4562
|
-
const listPath = call.args.path || '.';
|
|
4563
|
-
const command = runtime.platform === 'windows'
|
|
4564
|
-
? `dir /b "${path.join(this.currentProjectPath, listPath)}"`
|
|
4565
|
-
: `ls -la "${path.join(this.currentProjectPath, listPath)}"`;
|
|
4566
|
-
const shellResult = await this.tools.execute({
|
|
4567
|
-
tool: 'bash',
|
|
4568
|
-
args: { command, cwd: this.currentProjectPath },
|
|
4569
|
-
});
|
|
4570
|
-
if (shellResult.success && shellResult.output) {
|
|
4571
|
-
return {
|
|
4572
|
-
...result,
|
|
4573
|
-
suggestion: `list_dir failed for "${listPath}". Terminal listing:\n${shellResult.output}`,
|
|
4574
|
-
};
|
|
4575
|
-
}
|
|
4576
|
-
}
|
|
4577
|
-
return result;
|
|
4578
|
-
}
|
|
4579
3901
|
async executeToolCalls(toolCalls) {
|
|
4580
3902
|
if (!this.tools) {
|
|
4581
3903
|
throw new Error('Agent tools are not initialized.');
|
|
@@ -4585,20 +3907,15 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4585
3907
|
const verbose = !this.jsonOutput;
|
|
4586
3908
|
for (const call of toolCalls) {
|
|
4587
3909
|
if (verbose) {
|
|
4588
|
-
|
|
4589
|
-
const detail = target ? chalk.gray(` → ${String(target).replace(/\\/g, '/')}`) : '';
|
|
4590
|
-
console.log(chalk.cyan(`⚙ Executing: ${call.tool}`) + detail);
|
|
3910
|
+
console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
|
|
4591
3911
|
}
|
|
4592
|
-
getBridgeClient()?.emitToolCall({ tool: call.tool, args: call.args });
|
|
3912
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
|
|
4593
3913
|
let result = await this.tools.execute(call);
|
|
4594
|
-
if (!result.success) {
|
|
4595
|
-
result = await this.tryLocalToolFallback(call, result);
|
|
4596
|
-
}
|
|
4597
3914
|
// Phase 2: If a search tool failed (search_failed), retry with alternate approach
|
|
4598
3915
|
const searchStatus = result.metadata?.searchStatus;
|
|
4599
3916
|
if (call.tool === 'grep' && searchStatus === 'search_failed') {
|
|
4600
3917
|
if (verbose) {
|
|
4601
|
-
console.log(
|
|
3918
|
+
console.log(chalk_1.default.yellow(`⚠ Search backend failed, retrying with alternate method...`));
|
|
4602
3919
|
}
|
|
4603
3920
|
// Force Node-native fallback by re-executing with a note
|
|
4604
3921
|
const fallbackResult = await this.tools.execute({
|
|
@@ -4611,10 +3928,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4611
3928
|
}
|
|
4612
3929
|
const summary = this.formatToolResult(call, result);
|
|
4613
3930
|
if (verbose) {
|
|
4614
|
-
console.log(result.success ?
|
|
3931
|
+
console.log(result.success ? chalk_1.default.gray(summary) : chalk_1.default.red(summary));
|
|
4615
3932
|
}
|
|
4616
3933
|
this.messages.push({ role: 'system', content: summary });
|
|
4617
|
-
getBridgeClient()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
|
|
3934
|
+
(0, bridge_client_js_1.getBridgeClient)()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
|
|
4618
3935
|
// Phase 5: Track tool evidence for quality gates
|
|
4619
3936
|
const finalStatus = result.metadata?.searchStatus;
|
|
4620
3937
|
if (finalStatus === 'search_failed') {
|
|
@@ -4679,7 +3996,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4679
3996
|
this.messages = [...this.currentSession.messages];
|
|
4680
3997
|
this.sessionManager.save(this.currentSession);
|
|
4681
3998
|
if (!this.projectMemory) {
|
|
4682
|
-
this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
|
|
3999
|
+
this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
|
|
4683
4000
|
}
|
|
4684
4001
|
this.projectMemory.rememberConversation(this.messages, {
|
|
4685
4002
|
source: 'cli-session',
|
|
@@ -4698,7 +4015,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4698
4015
|
});
|
|
4699
4016
|
console.log(action);
|
|
4700
4017
|
const answer = await new Promise((resolve) => {
|
|
4701
|
-
rl.question(
|
|
4018
|
+
rl.question(chalk_1.default.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
|
|
4702
4019
|
});
|
|
4703
4020
|
rl.close();
|
|
4704
4021
|
const normalized = answer.trim().toLowerCase();
|
|
@@ -4721,3 +4038,4 @@ Now implement the missing fixes by editing the local workspace files. Use write/
|
|
|
4721
4038
|
return [...this.messages];
|
|
4722
4039
|
}
|
|
4723
4040
|
}
|
|
4041
|
+
exports.ChatCommand = ChatCommand;
|