vigthoria-cli 1.11.11 → 1.11.17
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/chat.d.ts +10 -0
- package/dist/commands/chat.js +281 -24
- package/dist/utils/agentRoute.d.ts +17 -0
- package/dist/utils/agentRoute.js +137 -0
- package/dist/utils/api.d.ts +2 -1
- package/dist/utils/api.js +71 -73
- package/dist/utils/deckEvents.d.ts +17 -0
- package/dist/utils/deckEvents.js +35 -0
- package/dist/utils/fastAgentRouter.d.ts +34 -0
- package/dist/utils/fastAgentRouter.js +171 -0
- package/dist/utils/requestIntent.d.ts +34 -0
- package/dist/utils/requestIntent.js +170 -0
- package/dist/utils/templateInstantPath.d.ts +18 -0
- package/dist/utils/templateInstantPath.js +121 -0
- package/dist/utils/v3-workspace-path.d.ts +7 -0
- package/dist/utils/v3-workspace-path.js +85 -0
- package/dist/utils/workspace-stream.js +9 -2
- package/package.json +2 -1
package/dist/commands/chat.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare class ChatCommand {
|
|
|
28
28
|
private tools;
|
|
29
29
|
private sessionManager;
|
|
30
30
|
private projectMemory;
|
|
31
|
+
private workspaceBrain;
|
|
31
32
|
private currentSession;
|
|
32
33
|
private agentMode;
|
|
33
34
|
private currentProjectPath;
|
|
@@ -51,6 +52,7 @@ export declare class ChatCommand {
|
|
|
51
52
|
private v3StreamedAnswerDisplayed;
|
|
52
53
|
private v3SeenToolCalls;
|
|
53
54
|
private v3SeenToolResults;
|
|
55
|
+
private lastAgentRoute;
|
|
54
56
|
private lastAgentRunOutcome;
|
|
55
57
|
private isJwtExpirationError;
|
|
56
58
|
private isNetworkError;
|
|
@@ -103,7 +105,11 @@ export declare class ChatCommand {
|
|
|
103
105
|
private buildTaskShapingInstructions;
|
|
104
106
|
private buildExecutionPrompt;
|
|
105
107
|
private isProjectBrainRuntimeDisabled;
|
|
108
|
+
private getWorkspaceBrainService;
|
|
109
|
+
private canPromptForWorkspaceIndex;
|
|
110
|
+
private promptWorkspaceIndex;
|
|
106
111
|
private buildProjectBrainRuntimeContext;
|
|
112
|
+
private buildWorkspaceBrainRuntimeContext;
|
|
107
113
|
private rememberBrainEvent;
|
|
108
114
|
private getPromptRuntimeContext;
|
|
109
115
|
private v3IterationCount;
|
|
@@ -168,6 +174,10 @@ export declare class ChatCommand {
|
|
|
168
174
|
private persistLocalLoopOutcome;
|
|
169
175
|
private runLocalAgentLoop;
|
|
170
176
|
private primeBypassedTargetFileContext;
|
|
177
|
+
/**
|
|
178
|
+
* Sub-second path for trivial HTML/hello-world: Template Service → index.html (no V3 planner).
|
|
179
|
+
*/
|
|
180
|
+
private tryTemplateInstantPath;
|
|
171
181
|
private tryDirectSingleFileFlow;
|
|
172
182
|
private isConfirmationFollowUp;
|
|
173
183
|
private taskRequiresWorkspaceChanges;
|
package/dist/commands/chat.js
CHANGED
|
@@ -11,10 +11,15 @@ import { BridgeClient, getBridgeClient } from '../utils/bridge-client.js';
|
|
|
11
11
|
import { WorkspaceWatcher } from '../utils/workspace-stream.js';
|
|
12
12
|
import { TaskDisplay } from '../utils/task-display.js';
|
|
13
13
|
import { ProjectMemoryService } from '../utils/project-memory.js';
|
|
14
|
+
import { WorkspaceBrainService } from '../utils/workspace-brain-service.js';
|
|
14
15
|
import { buildPersonaOverlay, normalizePersonaMode } from '../utils/persona.js';
|
|
15
16
|
import { runAgentSessionMenu, shouldShowAgentSessionMenu } from './agent-session-menu.js';
|
|
16
17
|
import { createLiveOutcome, evaluateExecutorSuccess, handleTaskEvent, isSubstantiveAgentAnswer, isToolEvidenceStubAnswer, normalizeAgentAnswerContent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
|
|
17
18
|
import { looksLikeMarkdownReport, renderMarkdownToTerminal, summarizeMarkdownReport, } from '../utils/terminalMarkdown.js';
|
|
19
|
+
import { emitDeckEvent, isDeckModeEnabled } from '../utils/deckEvents.js';
|
|
20
|
+
import { inferAgentTaskType as sharedInferAgentTaskType, isTrivialHtmlPageRequest, resolvePlannerAgentTimeoutMs, resolveWorkflowType, shouldSkipAnalysisRescue, taskRequiresWorkspaceChanges as promptRequiresWorkspaceChanges, buildExecutionHints, } from '../utils/requestIntent.js';
|
|
21
|
+
import { resolveAgentRoute } from '../utils/agentRoute.js';
|
|
22
|
+
import { runTemplateInstantPath } from '../utils/templateInstantPath.js';
|
|
18
23
|
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
19
24
|
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
|
|
20
25
|
if (!rawValue) {
|
|
@@ -47,6 +52,7 @@ export class ChatCommand {
|
|
|
47
52
|
tools = null;
|
|
48
53
|
sessionManager;
|
|
49
54
|
projectMemory = null;
|
|
55
|
+
workspaceBrain = null;
|
|
50
56
|
currentSession = null;
|
|
51
57
|
agentMode = false;
|
|
52
58
|
currentProjectPath = process.cwd();
|
|
@@ -71,6 +77,7 @@ export class ChatCommand {
|
|
|
71
77
|
v3StreamedAnswerDisplayed = false;
|
|
72
78
|
v3SeenToolCalls = new Set();
|
|
73
79
|
v3SeenToolResults = new Set();
|
|
80
|
+
lastAgentRoute = null;
|
|
74
81
|
// Last completed Agent run — used by /retry, /continue, and the final summary block.
|
|
75
82
|
lastAgentRunOutcome = null;
|
|
76
83
|
isJwtExpirationError(error) {
|
|
@@ -304,6 +311,16 @@ export class ChatCommand {
|
|
|
304
311
|
routeReason: 'explicit-model-selection',
|
|
305
312
|
};
|
|
306
313
|
}
|
|
314
|
+
if (this.lastAgentRoute?.path === 'template-instant') {
|
|
315
|
+
return {
|
|
316
|
+
selectedModel: 'agent',
|
|
317
|
+
explicitModel: false,
|
|
318
|
+
heavyTask,
|
|
319
|
+
cloudEligible,
|
|
320
|
+
cloudSelected: false,
|
|
321
|
+
routeReason: `template-instant [${this.lastAgentRoute.source}]`,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
307
324
|
if (requiresV3Workflow) {
|
|
308
325
|
return {
|
|
309
326
|
selectedModel: 'agent',
|
|
@@ -314,6 +331,16 @@ export class ChatCommand {
|
|
|
314
331
|
routeReason: 'v3-required-frontend-task',
|
|
315
332
|
};
|
|
316
333
|
}
|
|
334
|
+
if (this.lastAgentRoute?.source === 'balanced-4b' || this.lastAgentRoute?.source === 'merged') {
|
|
335
|
+
return {
|
|
336
|
+
selectedModel: 'agent',
|
|
337
|
+
explicitModel: false,
|
|
338
|
+
heavyTask,
|
|
339
|
+
cloudEligible,
|
|
340
|
+
cloudSelected: false,
|
|
341
|
+
routeReason: `balanced-4b-router → ${this.lastAgentRoute.taskKind}`,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
317
344
|
if (this.config.shouldUseCloudForHeavyTask(prompt)) {
|
|
318
345
|
return {
|
|
319
346
|
selectedModel: 'cloud',
|
|
@@ -550,14 +577,7 @@ export class ChatCommand {
|
|
|
550
577
|
return 'debugging';
|
|
551
578
|
if (this.isAnalysisLookupPrompt(prompt))
|
|
552
579
|
return 'analysis';
|
|
553
|
-
|
|
554
|
-
return 'analysis';
|
|
555
|
-
}
|
|
556
|
-
if (/\b(implement|build|create|write|fix|repair|scaffold|deploy|add feature)\b/i.test(prompt)
|
|
557
|
-
&& !/\b(analy[sz]e|analyse|audit|review|inspect)\b/i.test(prompt)) {
|
|
558
|
-
return /\b(game|html5|pacman|rogue|playable)\b/i.test(prompt) ? 'game-build' : 'implementation';
|
|
559
|
-
}
|
|
560
|
-
return 'analysis';
|
|
580
|
+
return sharedInferAgentTaskType(prompt);
|
|
561
581
|
}
|
|
562
582
|
buildTaskShapingInstructions(prompt) {
|
|
563
583
|
const instructions = [];
|
|
@@ -593,6 +613,45 @@ export class ChatCommand {
|
|
|
593
613
|
isProjectBrainRuntimeDisabled() {
|
|
594
614
|
return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_NO_BRAIN || process.env.VIGTHORIA_BRAIN_DISABLED || ''));
|
|
595
615
|
}
|
|
616
|
+
getWorkspaceBrainService() {
|
|
617
|
+
if (this.isProjectBrainRuntimeDisabled()) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
if (!this.workspaceBrain) {
|
|
622
|
+
this.workspaceBrain = new WorkspaceBrainService({
|
|
623
|
+
workspacePath: this.currentProjectPath,
|
|
624
|
+
apiBase: this.config.get('apiUrl'),
|
|
625
|
+
getAuthToken: () => this.config.get('authToken'),
|
|
626
|
+
autoIndex: false,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return this.workspaceBrain;
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
canPromptForWorkspaceIndex() {
|
|
636
|
+
if (this.jsonOutput || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
return !/^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_NO_INDEX_PROMPT || ''));
|
|
640
|
+
}
|
|
641
|
+
async promptWorkspaceIndex(fileCount, workspaceName) {
|
|
642
|
+
if (!this.canPromptForWorkspaceIndex()) {
|
|
643
|
+
return 'later';
|
|
644
|
+
}
|
|
645
|
+
const answer = await new Promise((resolve) => {
|
|
646
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
647
|
+
const prompt = `Index "${workspaceName}" for Vigthoria Brain (${fileCount} files) to improve large-context tasks? [y/N] `;
|
|
648
|
+
rl.question(chalk.cyan(prompt), (value) => {
|
|
649
|
+
rl.close();
|
|
650
|
+
resolve(value);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
return /^(y|yes)$/i.test(answer.trim()) ? 'index_now' : 'later';
|
|
654
|
+
}
|
|
596
655
|
buildProjectBrainRuntimeContext(prompt, source) {
|
|
597
656
|
if (this.isProjectBrainRuntimeDisabled()) {
|
|
598
657
|
return undefined;
|
|
@@ -619,6 +678,42 @@ export class ChatCommand {
|
|
|
619
678
|
return undefined;
|
|
620
679
|
}
|
|
621
680
|
}
|
|
681
|
+
async buildWorkspaceBrainRuntimeContext(prompt) {
|
|
682
|
+
const service = this.getWorkspaceBrainService();
|
|
683
|
+
if (!service) {
|
|
684
|
+
return {};
|
|
685
|
+
}
|
|
686
|
+
try {
|
|
687
|
+
const result = await service.ensureIndexed({
|
|
688
|
+
promptIfMissing: this.canPromptForWorkspaceIndex(),
|
|
689
|
+
askToIndex: (fileCount, workspaceName) => this.promptWorkspaceIndex(fileCount, workspaceName),
|
|
690
|
+
});
|
|
691
|
+
const status = service.getStatus();
|
|
692
|
+
const context = {
|
|
693
|
+
workspaceBrain: {
|
|
694
|
+
schema: 'vigthoria.workspace-brain.v1',
|
|
695
|
+
indexed: result.indexed,
|
|
696
|
+
prompted: result.prompted || null,
|
|
697
|
+
indexedFileCount: status.indexedFileCount || result.fileCount,
|
|
698
|
+
totalChunks: status.totalChunks || result.chunkCount,
|
|
699
|
+
indexedAt: status.meta?.indexedAt || null,
|
|
700
|
+
indexHash: status.meta?.indexHash || null,
|
|
701
|
+
},
|
|
702
|
+
};
|
|
703
|
+
const codebaseContext = service.buildCodebaseContext(prompt);
|
|
704
|
+
if (codebaseContext) {
|
|
705
|
+
context.codebaseContext = codebaseContext;
|
|
706
|
+
}
|
|
707
|
+
const accountBrainContext = await service.fetchAccountBrainContext().catch(() => '');
|
|
708
|
+
if (accountBrainContext) {
|
|
709
|
+
context.accountBrainContext = accountBrainContext;
|
|
710
|
+
}
|
|
711
|
+
return context;
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
return {};
|
|
715
|
+
}
|
|
716
|
+
}
|
|
622
717
|
rememberBrainEvent(type, text, mode) {
|
|
623
718
|
if (this.isProjectBrainRuntimeDisabled()) {
|
|
624
719
|
return;
|
|
@@ -640,6 +735,19 @@ export class ChatCommand {
|
|
|
640
735
|
const brainContext = this.buildProjectBrainRuntimeContext(prompt, this.operatorMode ? 'vigthoria-cli.operator' : this.agentMode ? 'vigthoria-cli.agent' : 'vigthoria-cli.chat');
|
|
641
736
|
if (brainContext) {
|
|
642
737
|
runtimeContext.vigthoriaBrain = brainContext;
|
|
738
|
+
if (typeof brainContext.context === 'string' && brainContext.context.trim()) {
|
|
739
|
+
runtimeContext.projectMemory = brainContext.context;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const workspaceBrainContext = await this.buildWorkspaceBrainRuntimeContext(prompt);
|
|
743
|
+
if (workspaceBrainContext.workspaceBrain) {
|
|
744
|
+
runtimeContext.workspaceBrain = workspaceBrainContext.workspaceBrain;
|
|
745
|
+
}
|
|
746
|
+
if (typeof workspaceBrainContext.codebaseContext === 'string' && workspaceBrainContext.codebaseContext.trim()) {
|
|
747
|
+
runtimeContext.codebaseContext = workspaceBrainContext.codebaseContext;
|
|
748
|
+
}
|
|
749
|
+
if (typeof workspaceBrainContext.accountBrainContext === 'string' && workspaceBrainContext.accountBrainContext.trim()) {
|
|
750
|
+
runtimeContext.accountBrainContext = workspaceBrainContext.accountBrainContext;
|
|
643
751
|
}
|
|
644
752
|
if (!this.isBrowserTaskPrompt(prompt)) {
|
|
645
753
|
return runtimeContext;
|
|
@@ -803,6 +911,11 @@ export class ChatCommand {
|
|
|
803
911
|
if (!isSubstantiveAgentAnswer(content)) {
|
|
804
912
|
return;
|
|
805
913
|
}
|
|
914
|
+
emitDeckEvent({ type: 'report_ready', markdown: content });
|
|
915
|
+
if (isDeckModeEnabled()) {
|
|
916
|
+
this.v3StreamedAnswerDisplayed = true;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
806
919
|
console.log('');
|
|
807
920
|
console.log(renderMarkdownToTerminal(content, this.getTerminalContentWidth()));
|
|
808
921
|
this.v3StreamedAnswerDisplayed = true;
|
|
@@ -875,6 +988,13 @@ export class ChatCommand {
|
|
|
875
988
|
}
|
|
876
989
|
this.v3SeenToolCalls.add(eventKey);
|
|
877
990
|
this.v3ToolCallCount += 1;
|
|
991
|
+
const toolName = event.name || event.tool || event.tool_name || '';
|
|
992
|
+
emitDeckEvent({
|
|
993
|
+
type: 'tool_start',
|
|
994
|
+
tool: toolName,
|
|
995
|
+
index: this.v3ToolCallCount,
|
|
996
|
+
target: event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '',
|
|
997
|
+
});
|
|
878
998
|
const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
|
|
879
999
|
const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
|
|
880
1000
|
const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
|
|
@@ -885,16 +1005,16 @@ export class ChatCommand {
|
|
|
885
1005
|
this.writeAgentActivityLine(this.fitTerminalText(stepLabel) + '\n');
|
|
886
1006
|
// Show extra detail for key tools
|
|
887
1007
|
const args = event.arguments || {};
|
|
888
|
-
const
|
|
889
|
-
if ((
|
|
1008
|
+
const toolNameForDetail = event.name || event.tool || '';
|
|
1009
|
+
if ((toolNameForDetail === 'write_file' || toolNameForDetail === 'edit_file') && typeof args.content === 'string') {
|
|
890
1010
|
const len = args.content.length;
|
|
891
1011
|
this.writeAgentActivityLine(chalk.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
|
|
892
1012
|
}
|
|
893
|
-
else if (
|
|
1013
|
+
else if (toolNameForDetail === 'bash' && typeof args.command === 'string') {
|
|
894
1014
|
const command = this.sanitizeServerPath(args.command);
|
|
895
1015
|
this.writeAgentActivityLine(chalk.gray(` ${this.fitTerminalText(`$ ${command}`, 6)}\n`));
|
|
896
1016
|
}
|
|
897
|
-
else if (
|
|
1017
|
+
else if (toolNameForDetail === 'read_file') {
|
|
898
1018
|
this.writeAgentActivityLine(chalk.gray(` ${this.fitTerminalText(`reading ${sanitizedTarget || 'file'}`, 6)}\n`));
|
|
899
1019
|
}
|
|
900
1020
|
spinner.text = `Running ${toolDesc}...`;
|
|
@@ -909,6 +1029,12 @@ export class ChatCommand {
|
|
|
909
1029
|
this.v3SeenToolResults.add(eventKey);
|
|
910
1030
|
const success = event.success !== false;
|
|
911
1031
|
const toolName = event.name || event.tool || '';
|
|
1032
|
+
emitDeckEvent({
|
|
1033
|
+
type: 'tool_end',
|
|
1034
|
+
tool: toolName,
|
|
1035
|
+
success,
|
|
1036
|
+
index: this.v3ToolCallCount,
|
|
1037
|
+
});
|
|
912
1038
|
const indicator = success ? chalk.green(' ✓') : chalk.red(' ✗');
|
|
913
1039
|
if (spinner.isSpinning)
|
|
914
1040
|
spinner.stop();
|
|
@@ -981,6 +1107,13 @@ export class ChatCommand {
|
|
|
981
1107
|
if (spinner.isSpinning)
|
|
982
1108
|
spinner.stop();
|
|
983
1109
|
this.displayV3StreamedAnswer();
|
|
1110
|
+
emitDeckEvent({
|
|
1111
|
+
type: 'run_complete',
|
|
1112
|
+
success: true,
|
|
1113
|
+
tools: event.tool_calls || this.v3ToolCallCount,
|
|
1114
|
+
iterations: event.iterations || this.v3IterationCount,
|
|
1115
|
+
elapsed: event.elapsed || '',
|
|
1116
|
+
});
|
|
984
1117
|
let statLine = `${iters} iterations, ${tools} tool calls`;
|
|
985
1118
|
if (elapsed)
|
|
986
1119
|
statLine += `, ${elapsed}`;
|
|
@@ -2031,7 +2164,20 @@ export class ChatCommand {
|
|
|
2031
2164
|
if (!this.tools) {
|
|
2032
2165
|
throw new Error('Agent tools are not initialized.');
|
|
2033
2166
|
}
|
|
2167
|
+
this.lastAgentRoute = await resolveAgentRoute(this.api, prompt);
|
|
2168
|
+
if (!this.jsonOutput) {
|
|
2169
|
+
const r = this.lastAgentRoute;
|
|
2170
|
+
const routerNote = r.routerLatencyMs
|
|
2171
|
+
? chalk.gray(` (${r.routerModel || 'balanced-4b'} ${r.routerLatencyMs}ms)`)
|
|
2172
|
+
: '';
|
|
2173
|
+
this.logger.debug(`Agent route: ${r.path} / ${r.taskKind} [${r.source}]${routerNote} — ${r.reason}`);
|
|
2174
|
+
}
|
|
2034
2175
|
const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
|
|
2176
|
+
const handledByTemplateInstant = await this.tryTemplateInstantPath(prompt);
|
|
2177
|
+
if (handledByTemplateInstant) {
|
|
2178
|
+
this.saveSession();
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2035
2181
|
const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(prompt);
|
|
2036
2182
|
if (handledByDirectFileFlow) {
|
|
2037
2183
|
this.saveSession();
|
|
@@ -2371,6 +2517,98 @@ export class ChatCommand {
|
|
|
2371
2517
|
this.messages.push({ role: 'system', content: readSummary });
|
|
2372
2518
|
}
|
|
2373
2519
|
}
|
|
2520
|
+
/**
|
|
2521
|
+
* Sub-second path for trivial HTML/hello-world: Template Service → index.html (no V3 planner).
|
|
2522
|
+
*/
|
|
2523
|
+
async tryTemplateInstantPath(prompt) {
|
|
2524
|
+
const route = this.lastAgentRoute;
|
|
2525
|
+
if (route?.path !== 'template-instant' && !isTrivialHtmlPageRequest(prompt)) {
|
|
2526
|
+
return false;
|
|
2527
|
+
}
|
|
2528
|
+
let workspacePath = this.currentProjectPath;
|
|
2529
|
+
try {
|
|
2530
|
+
const fromPrompt = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
|
|
2531
|
+
if (fromPrompt)
|
|
2532
|
+
workspacePath = fromPrompt;
|
|
2533
|
+
}
|
|
2534
|
+
catch {
|
|
2535
|
+
// use default workspace
|
|
2536
|
+
}
|
|
2537
|
+
if (!this.jsonOutput) {
|
|
2538
|
+
console.log();
|
|
2539
|
+
console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
|
|
2540
|
+
const r = this.lastAgentRoute;
|
|
2541
|
+
console.log(chalk.gray(`Reason: ${r?.reason || 'template-instant (trivial HTML page)'}`));
|
|
2542
|
+
console.log(chalk.gray(`Path: template-instant [${r?.source || 'regex'}]`));
|
|
2543
|
+
console.log(chalk.gray('Model: template-service'));
|
|
2544
|
+
console.log(chalk.gray('Cloud Eligible: false'));
|
|
2545
|
+
console.log(chalk.gray('Cloud Selected: false'));
|
|
2546
|
+
console.log(chalk.gray('━'.repeat(30)));
|
|
2547
|
+
console.log();
|
|
2548
|
+
console.log(chalk.cyan('⚡ Template instant path — matching template and writing index.html...'));
|
|
2549
|
+
}
|
|
2550
|
+
this.messages.push({ role: 'user', content: prompt });
|
|
2551
|
+
const mustComplete = route?.path === 'template-instant';
|
|
2552
|
+
let result = await runTemplateInstantPath(this.api, prompt, workspacePath, {
|
|
2553
|
+
allowFallback: true,
|
|
2554
|
+
});
|
|
2555
|
+
if (!result.ok && mustComplete) {
|
|
2556
|
+
result = await runTemplateInstantPath(this.api, prompt, workspacePath, {
|
|
2557
|
+
allowFallback: true,
|
|
2558
|
+
forceMinimalFallback: true,
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
if (!result.ok) {
|
|
2562
|
+
if (!this.jsonOutput) {
|
|
2563
|
+
console.log(chalk.yellow(`Template instant path skipped: ${result.error || 'no match'}`));
|
|
2564
|
+
}
|
|
2565
|
+
return false;
|
|
2566
|
+
}
|
|
2567
|
+
const summary = [
|
|
2568
|
+
`Created ${result.entryPath || 'index.html'} via template-instant`,
|
|
2569
|
+
result.templateName ? `(${result.templateName})` : '',
|
|
2570
|
+
result.processingMs ? `in ${Math.round(result.processingMs)}ms` : '',
|
|
2571
|
+
result.usedFallback ? '(minimal fallback)' : '',
|
|
2572
|
+
].filter(Boolean).join(' ');
|
|
2573
|
+
this.messages.push({ role: 'assistant', content: summary });
|
|
2574
|
+
const liveOutcome = createLiveOutcome();
|
|
2575
|
+
liveOutcome.requiresWorkspaceChanges = true;
|
|
2576
|
+
liveOutcome.workspaceHasOutput = true;
|
|
2577
|
+
liveOutcome.changedFileCount = 1;
|
|
2578
|
+
liveOutcome.tasksTotal = 1;
|
|
2579
|
+
liveOutcome.tasksSucceeded = 1;
|
|
2580
|
+
liveOutcome.answerContent = summary;
|
|
2581
|
+
const evaluation = evaluateExecutorSuccess(liveOutcome);
|
|
2582
|
+
this.lastAgentRunOutcome = {
|
|
2583
|
+
prompt,
|
|
2584
|
+
taskId: null,
|
|
2585
|
+
contextId: null,
|
|
2586
|
+
tasksSucceeded: 1,
|
|
2587
|
+
tasksTotal: 1,
|
|
2588
|
+
failedTaskIds: [],
|
|
2589
|
+
unfinishedTaskIds: [],
|
|
2590
|
+
qualityScore: null,
|
|
2591
|
+
qualityMissing: [],
|
|
2592
|
+
qualityBlockers: [],
|
|
2593
|
+
hasOutput: true,
|
|
2594
|
+
answerContent: summary,
|
|
2595
|
+
selfHealStatus: 'skipped',
|
|
2596
|
+
selfHealTool: null,
|
|
2597
|
+
plannerError: null,
|
|
2598
|
+
executorError: null,
|
|
2599
|
+
clientToolErrors: [],
|
|
2600
|
+
transportErrors: [],
|
|
2601
|
+
workspacePath: workspacePath || null,
|
|
2602
|
+
workspaceSyncIssue: null,
|
|
2603
|
+
finishedAt: Date.now(),
|
|
2604
|
+
};
|
|
2605
|
+
if (!this.jsonOutput) {
|
|
2606
|
+
console.log(chalk.green(`✓ ${summary}`));
|
|
2607
|
+
console.log(chalk.gray(' Run: vigthoria preview'));
|
|
2608
|
+
this.printAgentRunSummary(this.lastAgentRunOutcome, evaluation, 1);
|
|
2609
|
+
}
|
|
2610
|
+
return true;
|
|
2611
|
+
}
|
|
2374
2612
|
async tryDirectSingleFileFlow(prompt) {
|
|
2375
2613
|
if (!this.directPromptMode || !this.tools) {
|
|
2376
2614
|
return false;
|
|
@@ -2490,13 +2728,7 @@ export class ChatCommand {
|
|
|
2490
2728
|
return /^(ja|ja bitte|ja bitte mach das|mach das|bitte mach das|genau|ok|okay|yes|yes please|please do|do it|go ahead|continue|proceed|make it so)$/.test(normalized);
|
|
2491
2729
|
}
|
|
2492
2730
|
taskRequiresWorkspaceChanges(prompt) {
|
|
2493
|
-
|
|
2494
|
-
const readOnlyIntent = /\b(analy[sz]e|analyse|analysis|audit|review|inspect|proof|understand|summari[sz]e|scan|read[\s-]?only|where we left|what is|how does|show me|tell me|identify\s+gaps?|gap\s+analysis|production\s+blockers?)\b/i.test(text);
|
|
2495
|
-
const explicitWriteIntent = /\b(build|create|make|implement|complete|fix|repair|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor)\b/i.test(text);
|
|
2496
|
-
if (readOnlyIntent && !explicitWriteIntent) {
|
|
2497
|
-
return false;
|
|
2498
|
-
}
|
|
2499
|
-
return explicitWriteIntent && /\b(file|project|game|app|website|html5|frontend|component|feature|code|workspace|repo)\b/i.test(text);
|
|
2731
|
+
return promptRequiresWorkspaceChanges(prompt);
|
|
2500
2732
|
}
|
|
2501
2733
|
getPreviousActionablePrompt() {
|
|
2502
2734
|
if (this.lastActionableUserInput && !this.isConfirmationFollowUp(this.lastActionableUserInput)) {
|
|
@@ -2578,6 +2810,7 @@ export class ChatCommand {
|
|
|
2578
2810
|
this.v3StreamedAnswerDisplayed = false;
|
|
2579
2811
|
this.v3SeenToolCalls.clear();
|
|
2580
2812
|
this.v3SeenToolResults.clear();
|
|
2813
|
+
emitDeckEvent({ type: 'agent_start' });
|
|
2581
2814
|
const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], false);
|
|
2582
2815
|
taskDisplay.start(0);
|
|
2583
2816
|
const spinner = this.jsonOutput ? null : createSpinner({
|
|
@@ -2636,7 +2869,11 @@ export class ChatCommand {
|
|
|
2636
2869
|
}
|
|
2637
2870
|
};
|
|
2638
2871
|
const executionPrompt = this.buildExecutionPrompt(contextualPrompt);
|
|
2639
|
-
const agentTaskType = this.inferAgentTaskType(prompt);
|
|
2872
|
+
const agentTaskType = this.lastAgentRoute?.taskKind || this.inferAgentTaskType(prompt);
|
|
2873
|
+
const workflowType = resolveWorkflowType(agentTaskType, prompt);
|
|
2874
|
+
const executionHints = buildExecutionHints(agentTaskType, prompt, {
|
|
2875
|
+
fastPath: this.lastAgentRoute?.fastPath,
|
|
2876
|
+
});
|
|
2640
2877
|
const workspaceContext = {
|
|
2641
2878
|
workspacePath: workspacePath,
|
|
2642
2879
|
projectPath: workspacePath,
|
|
@@ -2672,10 +2909,14 @@ export class ChatCommand {
|
|
|
2672
2909
|
workspace: { path: workspacePath },
|
|
2673
2910
|
...workspaceContext,
|
|
2674
2911
|
agentTaskType,
|
|
2912
|
+
workflowType,
|
|
2913
|
+
executionHints,
|
|
2914
|
+
fastPath: this.lastAgentRoute?.fastPath,
|
|
2915
|
+
agentRoute: this.lastAgentRoute,
|
|
2675
2916
|
executionSurface: 'cli',
|
|
2676
2917
|
clientSurface: 'cli',
|
|
2677
2918
|
localMachineCapable: true,
|
|
2678
|
-
agentTimeoutMs: DEFAULT_V3_AGENT_TIMEOUT_MS,
|
|
2919
|
+
agentTimeoutMs: resolvePlannerAgentTimeoutMs(DEFAULT_V3_AGENT_TIMEOUT_MS, agentTaskType, prompt),
|
|
2679
2920
|
agentIdleTimeoutMs: DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS,
|
|
2680
2921
|
agentSoftTimeoutMs: DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS,
|
|
2681
2922
|
model: routingPolicy.selectedModel,
|
|
@@ -2768,7 +3009,7 @@ export class ChatCommand {
|
|
|
2768
3009
|
const previewGate = (response.metadata?.previewGate || null);
|
|
2769
3010
|
const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
|
|
2770
3011
|
const changedFileCount = response.changedFiles ? Object.keys(response.changedFiles).length : 0;
|
|
2771
|
-
const requiresWorkspaceChanges =
|
|
3012
|
+
const requiresWorkspaceChanges = promptRequiresWorkspaceChanges(prompt);
|
|
2772
3013
|
const answerContent = normalizeAgentAnswerContent(response.content, this.v3StreamedAnswerBuffer);
|
|
2773
3014
|
liveOutcome.changedFileCount = changedFileCount;
|
|
2774
3015
|
liveOutcome.requiresWorkspaceChanges = requiresWorkspaceChanges;
|
|
@@ -2847,7 +3088,11 @@ export class ChatCommand {
|
|
|
2847
3088
|
if (isToolEvidenceStubAnswer(finalAnswer)) {
|
|
2848
3089
|
finalAnswer = '';
|
|
2849
3090
|
}
|
|
2850
|
-
const needsAnalysisRescue = !
|
|
3091
|
+
const needsAnalysisRescue = !shouldSkipAnalysisRescue({
|
|
3092
|
+
tasksTotal: liveOutcome.tasksTotal,
|
|
3093
|
+
changedFileCount,
|
|
3094
|
+
requiresWorkspaceChanges,
|
|
3095
|
+
}) && !this.jsonOutput
|
|
2851
3096
|
&& (!executorSucceeded || !isSubstantiveAgentAnswer(finalAnswer));
|
|
2852
3097
|
if (needsAnalysisRescue) {
|
|
2853
3098
|
const rescued = await this.tryAnalysisLocalRescue(prompt, workspacePath);
|
|
@@ -3119,13 +3364,16 @@ export class ChatCommand {
|
|
|
3119
3364
|
this.v3LastActivity = Date.now();
|
|
3120
3365
|
this.v3StreamingStarted = false;
|
|
3121
3366
|
try {
|
|
3367
|
+
const retryTaskType = this.inferAgentTaskType(rawPrompt);
|
|
3122
3368
|
const retryResponse = await this.api.runV3AgentWorkflow(executionPrompt, {
|
|
3123
3369
|
workspace: { path: this.currentProjectPath },
|
|
3124
3370
|
...workspaceContext,
|
|
3125
3371
|
executionSurface: 'cli',
|
|
3126
3372
|
clientSurface: 'cli',
|
|
3127
3373
|
localMachineCapable: true,
|
|
3128
|
-
|
|
3374
|
+
agentTaskType: retryTaskType,
|
|
3375
|
+
workflowType: resolveWorkflowType(retryTaskType, rawPrompt),
|
|
3376
|
+
agentTimeoutMs: resolvePlannerAgentTimeoutMs(DEFAULT_V3_AGENT_TIMEOUT_MS, retryTaskType, rawPrompt),
|
|
3129
3377
|
agentIdleTimeoutMs: DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS,
|
|
3130
3378
|
agentSoftTimeoutMs: DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS,
|
|
3131
3379
|
model: routingPolicy.selectedModel,
|
|
@@ -3397,6 +3645,15 @@ export class ChatCommand {
|
|
|
3397
3645
|
*/
|
|
3398
3646
|
printAgentRunSummary(outcome, evaluation, changedFileCount) {
|
|
3399
3647
|
const executorSucceeded = evaluation.executorSucceeded;
|
|
3648
|
+
if (outcome.answerContent && isSubstantiveAgentAnswer(outcome.answerContent)) {
|
|
3649
|
+
const reportSummary = summarizeMarkdownReport(outcome.answerContent);
|
|
3650
|
+
emitDeckEvent({
|
|
3651
|
+
type: 'run_summary',
|
|
3652
|
+
success: executorSucceeded,
|
|
3653
|
+
title: reportSummary?.title || 'Agent run complete',
|
|
3654
|
+
sections: reportSummary?.sections || [],
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3400
3657
|
const bar = chalk.gray('─'.repeat(63));
|
|
3401
3658
|
const ok = chalk.green('✓');
|
|
3402
3659
|
const warn = chalk.yellow('⚠');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified agent routing: regex (instant) → optional Balanced 4B → V3 execution hints.
|
|
3
|
+
*/
|
|
4
|
+
import type { APIClient } from './api.js';
|
|
5
|
+
import { type AgentRoutePath } from './fastAgentRouter.js';
|
|
6
|
+
export type { AgentRoutePath };
|
|
7
|
+
export interface AgentRouteDecision {
|
|
8
|
+
path: AgentRoutePath;
|
|
9
|
+
taskKind: string;
|
|
10
|
+
fastPath?: string;
|
|
11
|
+
confidence: number;
|
|
12
|
+
reason: string;
|
|
13
|
+
source: 'regex-trivial' | 'regex' | 'balanced-4b' | 'merged';
|
|
14
|
+
routerLatencyMs?: number;
|
|
15
|
+
routerModel?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveAgentRoute(api: APIClient, prompt: string): Promise<AgentRouteDecision>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified agent routing: regex (instant) → optional Balanced 4B → V3 execution hints.
|
|
3
|
+
*/
|
|
4
|
+
import { inferAgentTaskType, isTrivialHtmlPageRequest, isContinueOrRetryPrompt, hasGameIntent, hasWebPageIntent, taskRequiresWorkspaceChanges, stripExecutionShaping, } from './requestIntent.js';
|
|
5
|
+
import { isFastInferenceRouterAvailable, routeWithBalanced4b, shouldUseBalancedRouter, } from './fastAgentRouter.js';
|
|
6
|
+
function normalizeTaskKind(value, fallback) {
|
|
7
|
+
const kind = String(value || fallback).trim().toLowerCase();
|
|
8
|
+
const allowed = new Set([
|
|
9
|
+
'web-build', 'game-build', 'implementation', 'analysis', 'debugging', 'verification', 'build', 'repair',
|
|
10
|
+
]);
|
|
11
|
+
if (allowed.has(kind)) {
|
|
12
|
+
return kind === 'build' ? 'implementation' : kind;
|
|
13
|
+
}
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
function mergeLlmRoute(regexTask, prompt, parsed) {
|
|
17
|
+
const llmPath = String(parsed.path || '').toLowerCase();
|
|
18
|
+
const llmTask = normalizeTaskKind(parsed.task_kind || parsed.taskKind, regexTask);
|
|
19
|
+
const llmFast = parsed.fast_path ?? parsed.fastPath ?? undefined;
|
|
20
|
+
const confidence = Number(parsed.confidence) || 0.75;
|
|
21
|
+
const reason = String(parsed.reason || 'balanced-4b router');
|
|
22
|
+
// Regex game/web signals override wrong LLM labels (hello-world → v3-agent bug).
|
|
23
|
+
if (isTrivialHtmlPageRequest(prompt) || (hasWebPageIntent(prompt) && !hasGameIntent(prompt) && llmTask === 'game-build')) {
|
|
24
|
+
return {
|
|
25
|
+
path: 'template-instant',
|
|
26
|
+
taskKind: 'web-build',
|
|
27
|
+
fastPath: 'template-instant',
|
|
28
|
+
confidence: Math.max(confidence, 0.92),
|
|
29
|
+
reason: 'regex web-page override',
|
|
30
|
+
source: 'merged',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (llmPath === 'template-instant' || llmPath === 'html-template' || llmFast === 'template-instant') {
|
|
34
|
+
if (hasWebPageIntent(prompt) || isTrivialHtmlPageRequest(prompt)) {
|
|
35
|
+
return {
|
|
36
|
+
path: 'template-instant',
|
|
37
|
+
taskKind: 'web-build',
|
|
38
|
+
fastPath: 'template-instant',
|
|
39
|
+
confidence,
|
|
40
|
+
reason,
|
|
41
|
+
source: 'merged',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (llmPath === 'analysis-only' || llmTask === 'analysis' || llmTask === 'debugging') {
|
|
46
|
+
if (!taskRequiresWorkspaceChanges(prompt)) {
|
|
47
|
+
return {
|
|
48
|
+
path: 'analysis-only',
|
|
49
|
+
taskKind: llmTask === 'debugging' ? 'debugging' : 'analysis',
|
|
50
|
+
confidence,
|
|
51
|
+
reason,
|
|
52
|
+
source: 'balanced-4b',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (hasGameIntent(prompt) || llmTask === 'game-build') {
|
|
57
|
+
return {
|
|
58
|
+
path: 'v3-agent',
|
|
59
|
+
taskKind: 'game-build',
|
|
60
|
+
confidence,
|
|
61
|
+
reason,
|
|
62
|
+
source: 'merged',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const taskKind = (regexTask === 'web-build' || regexTask === 'game-build') ? regexTask : (llmTask || regexTask);
|
|
66
|
+
return {
|
|
67
|
+
path: 'v3-agent',
|
|
68
|
+
taskKind,
|
|
69
|
+
fastPath: typeof llmFast === 'string' ? llmFast : undefined,
|
|
70
|
+
confidence,
|
|
71
|
+
reason,
|
|
72
|
+
source: 'balanced-4b',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export async function resolveAgentRoute(api, prompt) {
|
|
76
|
+
const stripped = stripExecutionShaping(prompt);
|
|
77
|
+
if (isContinueOrRetryPrompt(stripped)) {
|
|
78
|
+
const regexTask = inferAgentTaskType(stripped);
|
|
79
|
+
return {
|
|
80
|
+
path: 'v3-agent',
|
|
81
|
+
taskKind: regexTask === 'analysis' ? 'implementation' : regexTask,
|
|
82
|
+
confidence: 0.9,
|
|
83
|
+
reason: 'continue/retry follow-up (regex)',
|
|
84
|
+
source: 'regex',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (isTrivialHtmlPageRequest(stripped)) {
|
|
88
|
+
return {
|
|
89
|
+
path: 'template-instant',
|
|
90
|
+
taskKind: 'web-build',
|
|
91
|
+
fastPath: 'template-instant',
|
|
92
|
+
confidence: 1,
|
|
93
|
+
reason: 'trivial html page (regex)',
|
|
94
|
+
source: 'regex-trivial',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const regexTask = inferAgentTaskType(stripped);
|
|
98
|
+
if (!taskRequiresWorkspaceChanges(stripped) && (regexTask === 'analysis' || regexTask === 'debugging')) {
|
|
99
|
+
return {
|
|
100
|
+
path: 'analysis-only',
|
|
101
|
+
taskKind: regexTask,
|
|
102
|
+
confidence: 0.95,
|
|
103
|
+
reason: 'read-only analysis (regex)',
|
|
104
|
+
source: 'regex',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (hasGameIntent(stripped) && regexTask === 'game-build') {
|
|
108
|
+
return {
|
|
109
|
+
path: 'v3-agent',
|
|
110
|
+
taskKind: 'game-build',
|
|
111
|
+
confidence: 0.95,
|
|
112
|
+
reason: 'game build (regex)',
|
|
113
|
+
source: 'regex',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const selfHosted = api.getSelfHostedModelsApiUrl();
|
|
117
|
+
const tryLlm = shouldUseBalancedRouter(stripped, regexTask)
|
|
118
|
+
&& await isFastInferenceRouterAvailable(selfHosted);
|
|
119
|
+
if (tryLlm) {
|
|
120
|
+
const llm = await routeWithBalanced4b(stripped, selfHosted);
|
|
121
|
+
if (llm.ok && llm.parsed) {
|
|
122
|
+
const merged = mergeLlmRoute(regexTask, stripped, llm.parsed);
|
|
123
|
+
return {
|
|
124
|
+
...merged,
|
|
125
|
+
routerLatencyMs: llm.latencyMs,
|
|
126
|
+
routerModel: llm.model,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
path: 'v3-agent',
|
|
132
|
+
taskKind: regexTask,
|
|
133
|
+
confidence: 0.8,
|
|
134
|
+
reason: 'default v3 agent (regex)',
|
|
135
|
+
source: 'regex',
|
|
136
|
+
};
|
|
137
|
+
}
|