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/utils/api.d.ts
CHANGED
|
@@ -237,7 +237,8 @@ export declare class APIClient {
|
|
|
237
237
|
* on Windows / Node 25+.
|
|
238
238
|
*/
|
|
239
239
|
destroy(): void;
|
|
240
|
-
|
|
240
|
+
/** Exposed for Balanced 4B agent routing (local inference :8016). */
|
|
241
|
+
getSelfHostedModelsApiUrl(): string | null;
|
|
241
242
|
login(email: string, password: string): Promise<boolean>;
|
|
242
243
|
loginWithToken(token: string): Promise<boolean>;
|
|
243
244
|
private extractUserProfile;
|
package/dist/utils/api.js
CHANGED
|
@@ -12,6 +12,8 @@ import WebSocket from 'ws';
|
|
|
12
12
|
import { buildSemanticContext } from './context-ranker.js';
|
|
13
13
|
import { getChangedFiles } from './workspace-cache.js';
|
|
14
14
|
import { isSubstantiveAgentAnswer, isToolEvidenceStubAnswer } from './agentRunOutcome.js';
|
|
15
|
+
import { joinV3WorkspacePath, normalizeV3WorkspaceRelativePath } from './v3-workspace-path.js';
|
|
16
|
+
import { buildExecutionHints, isPlannerBuildTask, resolvePlannerAgentTimeoutMs } from './requestIntent.js';
|
|
15
17
|
export const VIGTHORIA_HUB_CREDITS_URL = 'https://hub.vigthoria.io/credits';
|
|
16
18
|
export const VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE = 'Vigthoria Server is temporarily not available. Please try again later. If the issue persists, please contact support.';
|
|
17
19
|
export class CLIError extends Error {
|
|
@@ -396,6 +398,7 @@ export class APIClient {
|
|
|
396
398
|
this.ws = null;
|
|
397
399
|
}
|
|
398
400
|
}
|
|
401
|
+
/** Exposed for Balanced 4B agent routing (local inference :8016). */
|
|
399
402
|
getSelfHostedModelsApiUrl() {
|
|
400
403
|
const configuredUrl = process.env.VIGTHORIA_SELF_HOSTED_MODELS_API_URL
|
|
401
404
|
|| this.config.get('selfHostedModelsApiUrl');
|
|
@@ -1446,6 +1449,25 @@ export class APIClient {
|
|
|
1446
1449
|
// files are provided inline in localWorkspaceSummary.workspaceFiles.
|
|
1447
1450
|
const effectiveWorkspacePath = serverWorkspacePath || null;
|
|
1448
1451
|
const needsHydration = !serverWorkspacePath && !!localWorkspacePath;
|
|
1452
|
+
const agentTaskType = resolvedContext.agentTaskType || 'general';
|
|
1453
|
+
const rawPrompt = resolvedContext.rawPrompt || resolvedContext.prompt || '';
|
|
1454
|
+
const executionHints = (resolvedContext.executionHints && typeof resolvedContext.executionHints === 'object')
|
|
1455
|
+
? { ...resolvedContext.executionHints }
|
|
1456
|
+
: buildExecutionHints(agentTaskType, rawPrompt, {
|
|
1457
|
+
fastPath: resolvedContext.fastPath,
|
|
1458
|
+
});
|
|
1459
|
+
if (!executionHints.task_kind) {
|
|
1460
|
+
executionHints.task_kind = agentTaskType;
|
|
1461
|
+
}
|
|
1462
|
+
if (!executionHints.classify_from) {
|
|
1463
|
+
executionHints.classify_from = rawPrompt;
|
|
1464
|
+
}
|
|
1465
|
+
if (resolvedContext.workflowType === 'analysis_only' || resolvedContext.workflowType === 'full') {
|
|
1466
|
+
executionHints.workflow_type = resolvedContext.workflowType;
|
|
1467
|
+
if (resolvedContext.workflowType === 'analysis_only') {
|
|
1468
|
+
executionHints.requires_file_changes = false;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1449
1471
|
const payload = {
|
|
1450
1472
|
workspace: this.buildPublicWorkspaceDescriptor(resolvedContext.workspace, {
|
|
1451
1473
|
localWorkspacePath,
|
|
@@ -1453,16 +1475,11 @@ export class APIClient {
|
|
|
1453
1475
|
}),
|
|
1454
1476
|
activeFile: resolvedContext.activeFile || null,
|
|
1455
1477
|
history: resolvedContext.history || [],
|
|
1456
|
-
agentTaskType
|
|
1457
|
-
|
|
1478
|
+
agentTaskType,
|
|
1479
|
+
workflowType: executionHints.workflow_type,
|
|
1480
|
+
rawPrompt,
|
|
1458
1481
|
contextualPrompt: resolvedContext.contextualPrompt || '',
|
|
1459
|
-
executionHints
|
|
1460
|
-
task_kind: resolvedContext.agentTaskType || 'general',
|
|
1461
|
-
requires_file_changes: resolvedContext.agentTaskType === 'analysis' || resolvedContext.agentTaskType === 'verification'
|
|
1462
|
-
? false
|
|
1463
|
-
: undefined,
|
|
1464
|
-
classify_from: resolvedContext.rawPrompt || resolvedContext.prompt || '',
|
|
1465
|
-
},
|
|
1482
|
+
executionHints,
|
|
1466
1483
|
model: resolvedModel,
|
|
1467
1484
|
requestedModel,
|
|
1468
1485
|
requestedModelResolved: resolvedModel,
|
|
@@ -1523,6 +1540,9 @@ export class APIClient {
|
|
|
1523
1540
|
let json = JSON.stringify(payload);
|
|
1524
1541
|
if (json.length <= LIMIT)
|
|
1525
1542
|
return json;
|
|
1543
|
+
if (!/^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_QUIET_CONTEXT_COMPACTION || ''))) {
|
|
1544
|
+
process.stderr.write(`Workspace context is large - sent a focused summary (${Math.round(LIMIT / 1000)}k limit).\n`);
|
|
1545
|
+
}
|
|
1526
1546
|
if (process.env.DEBUG || process.env.VIGTHORIA_DEBUG) {
|
|
1527
1547
|
process.stderr.write(`[context] Payload ${json.length} chars exceeds ${LIMIT} limit, compacting...\n`);
|
|
1528
1548
|
}
|
|
@@ -1651,6 +1671,25 @@ export class APIClient {
|
|
|
1651
1671
|
const localWorkspaceRef = localWorkspaceName ? `vigthoria://local-workspace/${localWorkspaceName}` : null;
|
|
1652
1672
|
const rawBrain = resolvedContext.vigthoriaBrain || resolvedContext.vigthoria_brain || null;
|
|
1653
1673
|
const dedupedBrain = rawBrain ? this.dedupeBrainPayload(rawBrain) : null;
|
|
1674
|
+
const agentTaskType = resolvedContext.agentTaskType || 'general';
|
|
1675
|
+
const rawPrompt = resolvedContext.rawPrompt || resolvedContext.prompt || '';
|
|
1676
|
+
const executionHints = (resolvedContext.executionHints && typeof resolvedContext.executionHints === 'object')
|
|
1677
|
+
? { ...resolvedContext.executionHints }
|
|
1678
|
+
: buildExecutionHints(agentTaskType, rawPrompt, {
|
|
1679
|
+
fastPath: resolvedContext.fastPath,
|
|
1680
|
+
});
|
|
1681
|
+
if (!executionHints.task_kind) {
|
|
1682
|
+
executionHints.task_kind = agentTaskType;
|
|
1683
|
+
}
|
|
1684
|
+
if (!executionHints.classify_from) {
|
|
1685
|
+
executionHints.classify_from = rawPrompt;
|
|
1686
|
+
}
|
|
1687
|
+
if (resolvedContext.workflowType === 'analysis_only' || resolvedContext.workflowType === 'full') {
|
|
1688
|
+
executionHints.workflow_type = resolvedContext.workflowType;
|
|
1689
|
+
if (resolvedContext.workflowType === 'analysis_only') {
|
|
1690
|
+
executionHints.requires_file_changes = false;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1654
1693
|
return JSON.stringify({
|
|
1655
1694
|
workspace: this.buildPublicWorkspaceDescriptor(resolvedContext.workspace, {
|
|
1656
1695
|
localWorkspacePath: targetPath,
|
|
@@ -1658,16 +1697,11 @@ export class APIClient {
|
|
|
1658
1697
|
}),
|
|
1659
1698
|
activeFile: resolvedContext.activeFile || null,
|
|
1660
1699
|
history: resolvedContext.history || [],
|
|
1661
|
-
agentTaskType
|
|
1662
|
-
|
|
1700
|
+
agentTaskType,
|
|
1701
|
+
workflowType: executionHints.workflow_type,
|
|
1702
|
+
rawPrompt,
|
|
1663
1703
|
contextualPrompt: resolvedContext.contextualPrompt || '',
|
|
1664
|
-
executionHints
|
|
1665
|
-
task_kind: resolvedContext.agentTaskType || 'general',
|
|
1666
|
-
requires_file_changes: resolvedContext.agentTaskType === 'analysis' || resolvedContext.agentTaskType === 'verification'
|
|
1667
|
-
? false
|
|
1668
|
-
: undefined,
|
|
1669
|
-
classify_from: resolvedContext.rawPrompt || resolvedContext.prompt || '',
|
|
1670
|
-
},
|
|
1704
|
+
executionHints,
|
|
1671
1705
|
executionSurface: resolvedContext.executionSurface || 'cli',
|
|
1672
1706
|
clientSurface: resolvedContext.clientSurface || 'cli',
|
|
1673
1707
|
localMachineCapable: resolvedContext.localMachineCapable !== false,
|
|
@@ -2660,7 +2694,7 @@ menu {
|
|
|
2660
2694
|
if (!relativePath && String(rawPath || '.').trim() !== '.') {
|
|
2661
2695
|
return null;
|
|
2662
2696
|
}
|
|
2663
|
-
const absolutePath =
|
|
2697
|
+
const absolutePath = relativePath ? joinV3WorkspacePath(rootPath, relativePath) : path.resolve(rootPath);
|
|
2664
2698
|
const resolvedRoot = path.resolve(rootPath);
|
|
2665
2699
|
if (absolutePath !== resolvedRoot && !absolutePath.startsWith(resolvedRoot + path.sep)) {
|
|
2666
2700
|
return null;
|
|
@@ -2864,7 +2898,7 @@ menu {
|
|
|
2864
2898
|
if (!relativePath) {
|
|
2865
2899
|
return false;
|
|
2866
2900
|
}
|
|
2867
|
-
const absolutePath =
|
|
2901
|
+
const absolutePath = joinV3WorkspacePath(rootPath, relativePath);
|
|
2868
2902
|
const resolvedRoot = path.resolve(rootPath);
|
|
2869
2903
|
if (absolutePath !== resolvedRoot && !absolutePath.startsWith(resolvedRoot + path.sep)) {
|
|
2870
2904
|
this.logger.warn(`Refusing to write V3 file outside workspace: ${rawPath}`);
|
|
@@ -2891,7 +2925,7 @@ menu {
|
|
|
2891
2925
|
if (!relativePath) {
|
|
2892
2926
|
return false;
|
|
2893
2927
|
}
|
|
2894
|
-
const absolutePath =
|
|
2928
|
+
const absolutePath = joinV3WorkspacePath(rootPath, relativePath);
|
|
2895
2929
|
const resolvedRoot = path.resolve(rootPath);
|
|
2896
2930
|
if (absolutePath !== resolvedRoot && !absolutePath.startsWith(resolvedRoot + path.sep)) {
|
|
2897
2931
|
this.logger.warn(`Refusing to delete V3 file outside workspace: ${rawPath}`);
|
|
@@ -2935,56 +2969,7 @@ menu {
|
|
|
2935
2969
|
}
|
|
2936
2970
|
}
|
|
2937
2971
|
normalizeAgentWorkspaceRelativePath(rawPath, rootPath) {
|
|
2938
|
-
|
|
2939
|
-
if (!input) {
|
|
2940
|
-
return '';
|
|
2941
|
-
}
|
|
2942
|
-
const safeRelative = (candidate) => {
|
|
2943
|
-
const stripped = String(candidate || '').replace(/^\/+/, '');
|
|
2944
|
-
if (!stripped || /^[a-zA-Z]:\//.test(stripped)) {
|
|
2945
|
-
return '';
|
|
2946
|
-
}
|
|
2947
|
-
const normalized = path.posix.normalize(stripped);
|
|
2948
|
-
if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../') || path.posix.isAbsolute(normalized)) {
|
|
2949
|
-
return '';
|
|
2950
|
-
}
|
|
2951
|
-
return normalized;
|
|
2952
|
-
};
|
|
2953
|
-
const internalWorkspacePatterns = [
|
|
2954
|
-
/^\/?var\/www\/\.vigthoria\/v3-temp\/vig-remote-[^/]+\/(.+)$/i,
|
|
2955
|
-
/^\.vigthoria\/v3-temp\/vig-remote-[^/]+\/(.+)$/i,
|
|
2956
|
-
/^\/?tmp\/vig-remote(?:-server)?-[^/]+\/(.+)$/i,
|
|
2957
|
-
/^\/?tmp\/vig-fork-[^/]+\/(.+)$/i,
|
|
2958
|
-
/^\/?home\/user\/(.+)$/i,
|
|
2959
|
-
/^\/?root\/(.+)$/i,
|
|
2960
|
-
];
|
|
2961
|
-
for (const pattern of internalWorkspacePatterns) {
|
|
2962
|
-
const match = input.match(pattern);
|
|
2963
|
-
if (match && match[1]) {
|
|
2964
|
-
return safeRelative(match[1]);
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
const normalizedRoot = String(rootPath || '').trim().replace(/\\/g, '/').replace(/\/+$/g, '');
|
|
2968
|
-
if (normalizedRoot) {
|
|
2969
|
-
const rootNoLeadingSlash = normalizedRoot.replace(/^\//, '');
|
|
2970
|
-
const rootBase = path.posix.basename(normalizedRoot);
|
|
2971
|
-
const prefixes = [
|
|
2972
|
-
`${normalizedRoot}/`,
|
|
2973
|
-
`${rootNoLeadingSlash}/`,
|
|
2974
|
-
`${rootBase}/`,
|
|
2975
|
-
];
|
|
2976
|
-
for (const prefix of prefixes) {
|
|
2977
|
-
if (input.startsWith(prefix)) {
|
|
2978
|
-
return safeRelative(input.slice(prefix.length));
|
|
2979
|
-
}
|
|
2980
|
-
}
|
|
2981
|
-
const embeddedRoot = `/${rootBase}/`;
|
|
2982
|
-
const embeddedIndex = input.indexOf(embeddedRoot);
|
|
2983
|
-
if (embeddedIndex >= 0) {
|
|
2984
|
-
return safeRelative(input.slice(embeddedIndex + embeddedRoot.length));
|
|
2985
|
-
}
|
|
2986
|
-
}
|
|
2987
|
-
return safeRelative(input);
|
|
2972
|
+
return normalizeV3WorkspaceRelativePath(rawPath, rootPath);
|
|
2988
2973
|
}
|
|
2989
2974
|
async ensureAgentFrontendPolish(message = '', context = {}) {
|
|
2990
2975
|
const rootPath = this.resolveAgentTargetPath(context);
|
|
@@ -3542,7 +3527,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3542
3527
|
this.activeV3StreamedFiles = streamedFiles;
|
|
3543
3528
|
this.activeV3StreamContext = context;
|
|
3544
3529
|
context.__v3StreamedFiles = streamedFiles;
|
|
3545
|
-
const idleTimeoutMs =
|
|
3530
|
+
const idleTimeoutMs = (() => {
|
|
3531
|
+
const base = context.agentIdleTimeoutMs || DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS;
|
|
3532
|
+
if (isPlannerBuildTask(String(context.agentTaskType || ''), String(context.rawPrompt || ''))) {
|
|
3533
|
+
const raw = process.env.VIGTHORIA_AGENT_PLANNER_IDLE_TIMEOUT_MS || '300000';
|
|
3534
|
+
const extended = Number.parseInt(raw, 10);
|
|
3535
|
+
return Math.max(base, Number.isFinite(extended) && extended > 0 ? extended : 300000);
|
|
3536
|
+
}
|
|
3537
|
+
return base;
|
|
3538
|
+
})();
|
|
3546
3539
|
try {
|
|
3547
3540
|
while (true) {
|
|
3548
3541
|
let chunk;
|
|
@@ -3562,6 +3555,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3562
3555
|
chunk = result;
|
|
3563
3556
|
break;
|
|
3564
3557
|
}
|
|
3558
|
+
if (this.pendingV3ClientToolTasks.size > 0) {
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3565
3561
|
if (this.hasAgentWorkspaceOutput(context)) {
|
|
3566
3562
|
const stalledError = new Error('V3 agent stream stalled after writing workspace output');
|
|
3567
3563
|
stalledError.name = 'AbortError';
|
|
@@ -3748,7 +3744,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3748
3744
|
&& context.localMachineCapable !== false;
|
|
3749
3745
|
const rescueEligibleSaaS = preferLocalV3
|
|
3750
3746
|
&& /(saas|dashboard|analytics|billing|team management|activity feed|login screen)/i.test(message);
|
|
3751
|
-
const timeoutMs =
|
|
3747
|
+
const timeoutMs = rescueEligibleSaaS
|
|
3748
|
+
? Math.min(resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || '')), 210000)
|
|
3749
|
+
: resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || ''));
|
|
3752
3750
|
const maxAttempts = preferLocalV3 ? 2 : 1;
|
|
3753
3751
|
let lastErrors = [];
|
|
3754
3752
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured events for Vigthoria Workbench (VIGTHORIA_DECK_MODE=1).
|
|
3
|
+
* Emitted as single lines: @vigthoria-deck:{json}
|
|
4
|
+
* Workbench strips these from terminal display and updates UI panels.
|
|
5
|
+
*/
|
|
6
|
+
export type DeckEventType = 'agent_start' | 'tool_start' | 'tool_end' | 'report_ready' | 'run_complete' | 'run_summary';
|
|
7
|
+
export interface DeckEvent {
|
|
8
|
+
type: DeckEventType;
|
|
9
|
+
ts: number;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export declare function isDeckModeEnabled(): boolean;
|
|
13
|
+
export declare function emitDeckEvent(event: Omit<DeckEvent, 'ts'> & {
|
|
14
|
+
ts?: number;
|
|
15
|
+
}): void;
|
|
16
|
+
export declare function parseDeckLine(line: string): DeckEvent | null;
|
|
17
|
+
export declare function stripDeckLines(text: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured events for Vigthoria Workbench (VIGTHORIA_DECK_MODE=1).
|
|
3
|
+
* Emitted as single lines: @vigthoria-deck:{json}
|
|
4
|
+
* Workbench strips these from terminal display and updates UI panels.
|
|
5
|
+
*/
|
|
6
|
+
const DECK_PREFIX = '@vigthoria-deck:';
|
|
7
|
+
export function isDeckModeEnabled() {
|
|
8
|
+
return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_DECK_MODE || ''));
|
|
9
|
+
}
|
|
10
|
+
export function emitDeckEvent(event) {
|
|
11
|
+
if (!isDeckModeEnabled())
|
|
12
|
+
return;
|
|
13
|
+
const payload = {
|
|
14
|
+
...event,
|
|
15
|
+
ts: event.ts ?? Date.now(),
|
|
16
|
+
};
|
|
17
|
+
process.stdout.write(`${DECK_PREFIX}${JSON.stringify(payload)}\n`);
|
|
18
|
+
}
|
|
19
|
+
export function parseDeckLine(line) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed.startsWith(DECK_PREFIX))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(trimmed.slice(DECK_PREFIX.length));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function stripDeckLines(text) {
|
|
31
|
+
return text
|
|
32
|
+
.split('\n')
|
|
33
|
+
.filter((line) => !line.trim().startsWith(DECK_PREFIX))
|
|
34
|
+
.join('\n');
|
|
35
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vigthoria Balanced 4B — fast agent path classifier (~0.7–2s on local inference :8016).
|
|
3
|
+
* Regex/trivial detection always wins; LLM router refines ambiguous build prompts only.
|
|
4
|
+
*/
|
|
5
|
+
export type AgentRoutePath = 'template-instant' | 'v3-agent' | 'analysis-only';
|
|
6
|
+
export interface BalancedRouterJson {
|
|
7
|
+
path?: string;
|
|
8
|
+
task_kind?: string;
|
|
9
|
+
taskKind?: string;
|
|
10
|
+
fast_path?: string | null;
|
|
11
|
+
fastPath?: string | null;
|
|
12
|
+
confidence?: number;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BalancedRouterResult {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
raw?: string;
|
|
18
|
+
parsed?: BalancedRouterJson;
|
|
19
|
+
latencyMs?: number;
|
|
20
|
+
model?: string;
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function parseBalancedRouterJson(raw: string): BalancedRouterJson | null;
|
|
25
|
+
/**
|
|
26
|
+
* Inference bases for Balanced 4B routing only.
|
|
27
|
+
* Local developer machines must opt in (env) — avoids ~1.5s localhost probe on every Windows turn.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getInferenceRouterBaseUrls(selfHostedUrl: string | null): string[];
|
|
30
|
+
export declare function isFastInferenceRouterAvailable(selfHostedUrl: string | null, timeoutMs?: number): Promise<boolean>;
|
|
31
|
+
export declare function routeWithBalanced4b(prompt: string, selfHostedUrl: string | null, options?: {
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}): Promise<BalancedRouterResult>;
|
|
34
|
+
export declare function shouldUseBalancedRouter(prompt: string, regexTaskKind?: string): boolean;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vigthoria Balanced 4B — fast agent path classifier (~0.7–2s on local inference :8016).
|
|
3
|
+
* Regex/trivial detection always wins; LLM router refines ambiguous build prompts only.
|
|
4
|
+
*/
|
|
5
|
+
const ROUTER_SYSTEM = `You are Vigthoria Agent Router. Reply with ONE JSON object only. No markdown, no HTML, no code fences.
|
|
6
|
+
|
|
7
|
+
Paths:
|
|
8
|
+
- template-instant: single trivial HTML page (hello world, one popup, simple static site) — use Template Service
|
|
9
|
+
- v3-agent: multi-file builds, games, refactors, complex implementation
|
|
10
|
+
- analysis-only: read-only review/audit/explain, no workspace writes
|
|
11
|
+
|
|
12
|
+
task_kind: web-build | game-build | implementation | analysis | debugging
|
|
13
|
+
fast_path: "template-instant" or null
|
|
14
|
+
|
|
15
|
+
Example: {"path":"template-instant","task_kind":"web-build","fast_path":"template-instant","confidence":0.95,"reason":"trivial html popup page"}`;
|
|
16
|
+
const INFERENCE_MODEL_IDS = [
|
|
17
|
+
'vigthoria-balanced-4b',
|
|
18
|
+
'vigthoria-v3-balanced-4b',
|
|
19
|
+
];
|
|
20
|
+
function normalizePath(value) {
|
|
21
|
+
const p = String(value || '').trim().toLowerCase();
|
|
22
|
+
if (p === 'template-instant' || p === 'template' || p === 'html-template') {
|
|
23
|
+
return 'template-instant';
|
|
24
|
+
}
|
|
25
|
+
if (p === 'analysis-only' || p === 'analysis') {
|
|
26
|
+
return 'analysis-only';
|
|
27
|
+
}
|
|
28
|
+
if (p === 'v3-agent' || p === 'v3' || p === 'agent') {
|
|
29
|
+
return 'v3-agent';
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
export function parseBalancedRouterJson(raw) {
|
|
34
|
+
const text = String(raw || '').trim();
|
|
35
|
+
if (!text)
|
|
36
|
+
return null;
|
|
37
|
+
const start = text.indexOf('{');
|
|
38
|
+
if (start < 0)
|
|
39
|
+
return null;
|
|
40
|
+
let depth = 0;
|
|
41
|
+
for (let i = start; i < text.length; i += 1) {
|
|
42
|
+
if (text[i] === '{')
|
|
43
|
+
depth += 1;
|
|
44
|
+
if (text[i] === '}') {
|
|
45
|
+
depth -= 1;
|
|
46
|
+
if (depth === 0) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(text.slice(start, i + 1));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Inference bases for Balanced 4B routing only.
|
|
60
|
+
* Local developer machines must opt in (env) — avoids ~1.5s localhost probe on every Windows turn.
|
|
61
|
+
*/
|
|
62
|
+
export function getInferenceRouterBaseUrls(selfHostedUrl) {
|
|
63
|
+
const urls = [
|
|
64
|
+
process.env.VIGTHORIA_INFERENCE_URL,
|
|
65
|
+
process.env.VIGTHORIA_SELF_HOSTED_MODELS_API_URL,
|
|
66
|
+
selfHostedUrl,
|
|
67
|
+
].filter(Boolean).map((u) => String(u).replace(/\/$/, ''));
|
|
68
|
+
const allowLocalhost = process.env.VIGTHORIA_ENABLE_LOCAL_INFERENCE_ROUTER === '1'
|
|
69
|
+
|| process.env.VIGTHORIA_AGENT_LLM_ROUTER === '1'
|
|
70
|
+
|| process.platform === 'linux';
|
|
71
|
+
if (allowLocalhost) {
|
|
72
|
+
urls.push('http://127.0.0.1:8016');
|
|
73
|
+
}
|
|
74
|
+
return [...new Set(urls)];
|
|
75
|
+
}
|
|
76
|
+
let inferenceHealthCache = null;
|
|
77
|
+
export async function isFastInferenceRouterAvailable(selfHostedUrl, timeoutMs = 400) {
|
|
78
|
+
if (process.env.VIGTHORIA_AGENT_LLM_ROUTER === '0') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
if (inferenceHealthCache && now - inferenceHealthCache.checkedAt < 60_000) {
|
|
83
|
+
return inferenceHealthCache.ok;
|
|
84
|
+
}
|
|
85
|
+
for (const base of getInferenceRouterBaseUrls(selfHostedUrl)) {
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(`${base}/health`, { signal: controller.signal });
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
if (response.ok) {
|
|
92
|
+
inferenceHealthCache = { ok: true, checkedAt: now };
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
inferenceHealthCache = { ok: false, checkedAt: now };
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
export async function routeWithBalanced4b(prompt, selfHostedUrl, options = {}) {
|
|
104
|
+
const timeoutMs = options.timeoutMs ?? Number.parseInt(process.env.VIGTHORIA_AGENT_ROUTER_TIMEOUT_MS || '8000', 10);
|
|
105
|
+
const bases = getInferenceRouterBaseUrls(selfHostedUrl);
|
|
106
|
+
let lastError = 'Inference router unreachable';
|
|
107
|
+
for (const base of bases) {
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
110
|
+
const started = Date.now();
|
|
111
|
+
for (const model of INFERENCE_MODEL_IDS) {
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(`${base}/v1/chat/completions`, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
model,
|
|
118
|
+
messages: [
|
|
119
|
+
{ role: 'system', content: ROUTER_SYSTEM },
|
|
120
|
+
{ role: 'user', content: String(prompt || '').slice(0, 1200) },
|
|
121
|
+
],
|
|
122
|
+
max_tokens: 96,
|
|
123
|
+
temperature: 0,
|
|
124
|
+
stream: false,
|
|
125
|
+
}),
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
});
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
const payload = await response.json().catch(() => null);
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
lastError = `HTTP ${response.status} from ${base}`;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const raw = payload?.choices?.[0]?.message?.content || '';
|
|
135
|
+
const parsed = parseBalancedRouterJson(raw);
|
|
136
|
+
if (!parsed) {
|
|
137
|
+
lastError = 'Router returned non-JSON';
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
raw,
|
|
143
|
+
parsed,
|
|
144
|
+
latencyMs: Date.now() - started,
|
|
145
|
+
model,
|
|
146
|
+
endpoint: base,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
}
|
|
155
|
+
return { ok: false, error: lastError };
|
|
156
|
+
}
|
|
157
|
+
export function shouldUseBalancedRouter(prompt, regexTaskKind = '') {
|
|
158
|
+
if (process.env.VIGTHORIA_AGENT_LLM_ROUTER === '0') {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const kind = String(regexTaskKind || '').toLowerCase();
|
|
162
|
+
// Regex already resolved clear build families — LLM must not override (e.g. landing → implementation).
|
|
163
|
+
if (kind === 'web-build' || kind === 'game-build') {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
const text = String(prompt || '').trim();
|
|
167
|
+
if (text.length > 700 || text.length < 12) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared prompt intent detection for CLI routing, V3 hints, and rescue gating.
|
|
3
|
+
* Keep aligned with V3-Code-Agent/agent.py _classify_request heuristics.
|
|
4
|
+
*/
|
|
5
|
+
/** Strip CLI-appended platform / mode shaping so it cannot flip quality profiles. */
|
|
6
|
+
export declare function stripExecutionShaping(prompt: string): string;
|
|
7
|
+
export declare function hasBuildVerb(prompt: string): boolean;
|
|
8
|
+
export declare function hasArtifactNoun(prompt: string): boolean;
|
|
9
|
+
export declare function hasGameIntent(prompt: string): boolean;
|
|
10
|
+
export declare function isContinueOrRetryPrompt(prompt: string): boolean;
|
|
11
|
+
export declare function hasWebPageIntent(prompt: string): boolean;
|
|
12
|
+
export declare function isTrivialHtmlPageRequest(prompt: string): boolean;
|
|
13
|
+
export declare function hasCloneBuildIntent(prompt: string): boolean;
|
|
14
|
+
export declare function hasExplicitWriteIntent(prompt: string): boolean;
|
|
15
|
+
export declare function taskRequiresWorkspaceChanges(prompt: string): boolean;
|
|
16
|
+
export declare function inferAgentTaskType(prompt: string): string;
|
|
17
|
+
export declare function shouldSkipAnalysisRescue(liveOutcome: {
|
|
18
|
+
tasksTotal?: number;
|
|
19
|
+
changedFileCount?: number;
|
|
20
|
+
requiresWorkspaceChanges?: boolean;
|
|
21
|
+
}): boolean;
|
|
22
|
+
export declare function isPlannerBuildTask(agentTaskType: string, prompt: string): boolean;
|
|
23
|
+
export declare function resolvePlannerAgentTimeoutMs(baseTimeoutMs: number, agentTaskType: string, prompt: string): number;
|
|
24
|
+
/** V3 workflow mode: suppress write path when the user did not ask for file changes. */
|
|
25
|
+
export declare function resolveWorkflowType(agentTaskType: string, prompt: string): 'analysis_only' | 'full';
|
|
26
|
+
export declare function buildExecutionHints(agentTaskType: string, prompt: string, options?: {
|
|
27
|
+
fastPath?: string;
|
|
28
|
+
}): {
|
|
29
|
+
task_kind: string;
|
|
30
|
+
requires_file_changes: boolean | undefined;
|
|
31
|
+
classify_from: string;
|
|
32
|
+
workflow_type: 'analysis_only' | 'full';
|
|
33
|
+
fast_path?: string;
|
|
34
|
+
};
|