vigthoria-cli 1.11.47 → 1.12.1
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 +2 -0
- package/dist/commands/agent-session-menu.js +22 -0
- package/dist/commands/chat.js +20 -1
- package/dist/commands/index.js +6 -0
- package/dist/commands/v4.d.ts +24 -0
- package/dist/commands/v4.js +105 -0
- package/dist/index.js +39 -1
- package/dist/utils/templateInstantPath.js +9 -1
- package/package.json +1 -1
|
@@ -3,12 +3,14 @@ export interface AgentSessionConfig {
|
|
|
3
3
|
debugMode: boolean;
|
|
4
4
|
autoApprove: boolean;
|
|
5
5
|
autoSave: boolean;
|
|
6
|
+
engine: 'v3' | 'v4';
|
|
6
7
|
}
|
|
7
8
|
export interface AgentSessionMenuDefaults {
|
|
8
9
|
workspacePath?: string;
|
|
9
10
|
debugMode?: boolean;
|
|
10
11
|
autoApprove?: boolean;
|
|
11
12
|
autoSave?: boolean;
|
|
13
|
+
engine?: 'v3' | 'v4';
|
|
12
14
|
}
|
|
13
15
|
export interface AgentSessionMenuOptions {
|
|
14
16
|
prompt?: string;
|
|
@@ -90,12 +90,34 @@ export async function runAgentSessionMenu(defaults = {}) {
|
|
|
90
90
|
debugMode: defaults.debugMode === true,
|
|
91
91
|
autoApprove: defaults.autoApprove === true,
|
|
92
92
|
autoSave: defaults.autoSave !== false,
|
|
93
|
+
engine: defaults.engine === 'v4' ? 'v4' : 'v3',
|
|
93
94
|
};
|
|
94
95
|
console.log();
|
|
95
96
|
console.log(chalk.cyan('═══ Agent Session Setup ═══'));
|
|
96
97
|
console.log(chalk.gray(fitLine(`Workspace: ${session.workspacePath}`)));
|
|
97
98
|
console.log(chalk.gray(fitLine(`Debug: ${session.debugMode ? 'on' : 'off'} | Auto-approve: ${session.autoApprove ? 'on' : 'off'} | Auto-save: ${session.autoSave ? 'on' : 'off'}`)));
|
|
98
99
|
console.log();
|
|
100
|
+
const { engine } = await inquirer.prompt([
|
|
101
|
+
{
|
|
102
|
+
type: 'list',
|
|
103
|
+
name: 'engine',
|
|
104
|
+
message: 'Agent engine',
|
|
105
|
+
default: session.engine,
|
|
106
|
+
choices: [
|
|
107
|
+
{ name: 'V3 Code Agent (default, stable)', value: 'v3' },
|
|
108
|
+
{ name: 'V4 Operating Agent (experimental, requires local V4-Operating-Agent checkout)', value: 'v4' },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
session.engine = engine;
|
|
113
|
+
if (session.engine === 'v4') {
|
|
114
|
+
console.log();
|
|
115
|
+
console.log(chalk.cyan('── Session ready ──'));
|
|
116
|
+
console.log(chalk.gray(fitLine(`Workspace: ${session.workspacePath}`)));
|
|
117
|
+
console.log(chalk.gray('Engine: V4 Operating Agent'));
|
|
118
|
+
console.log();
|
|
119
|
+
return session;
|
|
120
|
+
}
|
|
99
121
|
const { action } = await inquirer.prompt([
|
|
100
122
|
{
|
|
101
123
|
type: 'list',
|
package/dist/commands/chat.js
CHANGED
|
@@ -14,6 +14,7 @@ import { ProjectMemoryService } from '../utils/project-memory.js';
|
|
|
14
14
|
import { WorkspaceBrainService } from '../utils/workspace-brain-service.js';
|
|
15
15
|
import { buildPersonaOverlay, normalizePersonaMode } from '../utils/persona.js';
|
|
16
16
|
import { runAgentSessionMenu, shouldShowAgentSessionMenu } from './agent-session-menu.js';
|
|
17
|
+
import { V4Command } from './v4.js';
|
|
17
18
|
import { createLiveOutcome, evaluateExecutorSuccess, handleTaskEvent, isSubstantiveAgentAnswer, isToolEvidenceStubAnswer, normalizeAgentAnswerContent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
|
|
18
19
|
import { looksLikeMarkdownReport, renderMarkdownToTerminal, summarizeMarkdownReport, } from '../utils/terminalMarkdown.js';
|
|
19
20
|
import { emitDeckEvent, isDeckModeEnabled } from '../utils/deckEvents.js';
|
|
@@ -1503,6 +1504,11 @@ export class ChatCommand {
|
|
|
1503
1504
|
if (sessionConfig.debugMode) {
|
|
1504
1505
|
process.env.DEBUG = process.env.DEBUG || 'vigthoria:*';
|
|
1505
1506
|
}
|
|
1507
|
+
if (sessionConfig.engine === 'v4') {
|
|
1508
|
+
const v4 = new V4Command(this.config, this.logger);
|
|
1509
|
+
await v4.run({ v4Path: process.env.VIGTHORIA_V4_AGENT_DIR });
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1506
1512
|
console.log(chalk.green(this.fitTerminalText(`Using workspace: ${this.currentProjectPath}`)));
|
|
1507
1513
|
}
|
|
1508
1514
|
if (this.jsonOutput && !options.prompt && !options.retry && !options.continue) {
|
|
@@ -3913,7 +3919,20 @@ export class ChatCommand {
|
|
|
3913
3919
|
}
|
|
3914
3920
|
if (trimmed.startsWith('/model ')) {
|
|
3915
3921
|
this.currentModel = trimmed.slice(7).trim() || this.currentModel;
|
|
3916
|
-
this
|
|
3922
|
+
// BUGFIX: previously this unconditionally set modelExplicitlySelected = true,
|
|
3923
|
+
// which permanently locked resolveAgentExecutionPolicy() into its
|
|
3924
|
+
// 'explicit-model-selection' branch for the rest of the session -- even for
|
|
3925
|
+
// light chat aliases like 'balanced'/'fast'. Since 'balanced' resolves
|
|
3926
|
+
// server-side to vigthoria-fast-9b, every subsequent /retry, /continue, or
|
|
3927
|
+
// agent/operator task silently executed on the 9b model instead of the
|
|
3928
|
+
// expected 35b executor, causing repeated "0 tool calls" task failures.
|
|
3929
|
+
// Reuse the same hard-explicit rule applied at CLI startup so agent-mode
|
|
3930
|
+
// aliases don't lock out the dispatcher's auto-upgrade-to-agent-model logic.
|
|
3931
|
+
this.modelExplicitlySelected = this.isHardExplicitAgentModelSelection({
|
|
3932
|
+
model: this.currentModel,
|
|
3933
|
+
agent: this.agentMode,
|
|
3934
|
+
operator: this.operatorMode,
|
|
3935
|
+
});
|
|
3917
3936
|
console.log(chalk.yellow(`Model changed to: ${this.currentModel}`));
|
|
3918
3937
|
if (this.currentSession) {
|
|
3919
3938
|
this.currentSession.model = this.currentModel;
|
package/dist/commands/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { ReplayCommand } from './replay.js';
|
|
|
17
17
|
import { RepoCommand } from './repo.js';
|
|
18
18
|
import { ReviewCommand } from './review.js';
|
|
19
19
|
import { WorkflowCommand } from './workflow.js';
|
|
20
|
+
import { V4Command } from './v4.js';
|
|
20
21
|
function isRegisterableCommand(value) {
|
|
21
22
|
return Boolean(value && typeof value === 'object');
|
|
22
23
|
}
|
|
@@ -114,6 +115,11 @@ const commandRegistry = [
|
|
|
114
115
|
name: 'bridge',
|
|
115
116
|
handler: createClassRegistrar(BridgeCommand),
|
|
116
117
|
},
|
|
118
|
+
{
|
|
119
|
+
name: 'v4',
|
|
120
|
+
aliases: ['v4-agent'],
|
|
121
|
+
handler: createClassRegistrar(V4Command),
|
|
122
|
+
},
|
|
117
123
|
{
|
|
118
124
|
name: 'workflow',
|
|
119
125
|
handler: createClassRegistrar(WorkflowCommand),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Config } from '../utils/config.js';
|
|
3
|
+
import { Logger } from '../utils/logger.js';
|
|
4
|
+
type V4Options = {
|
|
5
|
+
provider?: 'cloud' | 'v3_local' | 'byok';
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
model?: string;
|
|
9
|
+
transport?: 'auto' | 'ws' | 'sse';
|
|
10
|
+
harnessStreamUrl?: string;
|
|
11
|
+
harnessControlUrl?: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
nonInteractive?: boolean;
|
|
14
|
+
python?: string;
|
|
15
|
+
v4Path?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare class V4Command {
|
|
18
|
+
private readonly _config;
|
|
19
|
+
private readonly logger;
|
|
20
|
+
constructor(_config: Config, logger: Logger);
|
|
21
|
+
register(program: Command): void;
|
|
22
|
+
run(options: V4Options): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
const DEFAULT_V4_AGENT_DIR_UNIX = '/var/www/V4-Operating-Agent';
|
|
6
|
+
function defaultV4PythonBin() {
|
|
7
|
+
return process.env.VIGTHORIA_V4_PYTHON || (process.platform === 'win32' ? 'python' : 'python3');
|
|
8
|
+
}
|
|
9
|
+
function defaultV4AgentDir() {
|
|
10
|
+
if (process.env.VIGTHORIA_V4_AGENT_DIR) {
|
|
11
|
+
return process.env.VIGTHORIA_V4_AGENT_DIR;
|
|
12
|
+
}
|
|
13
|
+
// The historical hardcoded default only ever existed on this project's Linux
|
|
14
|
+
// dev/test boxes. Never assume it on Windows/macOS — only use it as a
|
|
15
|
+
// fallback when it actually exists on disk, so real users get a clear
|
|
16
|
+
// "not found" error instead of a silently-wrong path.
|
|
17
|
+
if (process.platform !== 'win32' && fs.existsSync(DEFAULT_V4_AGENT_DIR_UNIX)) {
|
|
18
|
+
return DEFAULT_V4_AGENT_DIR_UNIX;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
export class V4Command {
|
|
23
|
+
_config;
|
|
24
|
+
logger;
|
|
25
|
+
constructor(_config, logger) {
|
|
26
|
+
this._config = _config;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
register(program) {
|
|
30
|
+
program
|
|
31
|
+
.command('v4')
|
|
32
|
+
.alias('v4-agent')
|
|
33
|
+
.description('Launch Vigthoria V4 Operating Agent (DeerFlow 2.0 harness)')
|
|
34
|
+
.option('--provider <provider>', 'Provider: cloud | v3_local | byok')
|
|
35
|
+
.option('--api-key <key>', 'BYOK API key for non-interactive mode')
|
|
36
|
+
.option('--base-url <url>', 'Provider base URL override')
|
|
37
|
+
.option('--model <name>', 'Provider model override')
|
|
38
|
+
.option('--transport <mode>', 'Bridge transport: auto | ws | sse', 'auto')
|
|
39
|
+
.option('--harness-stream-url <url>', 'Override harness stream URL')
|
|
40
|
+
.option('--harness-control-url <url>', 'Override harness control URL')
|
|
41
|
+
.option('--session-id <id>', 'Explicit V4 session id')
|
|
42
|
+
.option('--non-interactive', 'Disable interactive menu and use CLI options', false)
|
|
43
|
+
.option('--python <bin>', 'Python binary to use', defaultV4PythonBin())
|
|
44
|
+
.option('--v4-path <path>', 'V4 agent root path (or set VIGTHORIA_V4_AGENT_DIR)', defaultV4AgentDir())
|
|
45
|
+
.action(async (options) => {
|
|
46
|
+
await this.run(options);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async run(options) {
|
|
50
|
+
const resolvedV4Path = options.v4Path || defaultV4AgentDir();
|
|
51
|
+
if (!resolvedV4Path) {
|
|
52
|
+
throw Object.assign(new Error('V4 agent path not set. Pass --v4-path <dir> or set the VIGTHORIA_V4_AGENT_DIR environment variable.'), { skipPathScrub: true });
|
|
53
|
+
}
|
|
54
|
+
const v4Path = path.resolve(String(resolvedV4Path));
|
|
55
|
+
const pythonBin = String(options.python || defaultV4PythonBin());
|
|
56
|
+
const mainScript = path.join(v4Path, 'main_agent.py');
|
|
57
|
+
if (!fs.existsSync(mainScript)) {
|
|
58
|
+
throw Object.assign(new Error(`V4 launcher not found: ${mainScript}. Check --v4-path/VIGTHORIA_V4_AGENT_DIR points at a valid V4-Operating-Agent checkout.`), { skipPathScrub: true });
|
|
59
|
+
}
|
|
60
|
+
const args = [mainScript];
|
|
61
|
+
if (options.nonInteractive) {
|
|
62
|
+
args.push('--non-interactive');
|
|
63
|
+
}
|
|
64
|
+
if (options.provider) {
|
|
65
|
+
args.push('--provider', String(options.provider));
|
|
66
|
+
}
|
|
67
|
+
if (options.apiKey) {
|
|
68
|
+
args.push('--api-key', String(options.apiKey));
|
|
69
|
+
}
|
|
70
|
+
if (options.baseUrl) {
|
|
71
|
+
args.push('--base-url', String(options.baseUrl));
|
|
72
|
+
}
|
|
73
|
+
if (options.model) {
|
|
74
|
+
args.push('--model', String(options.model));
|
|
75
|
+
}
|
|
76
|
+
if (options.transport) {
|
|
77
|
+
args.push('--transport', String(options.transport));
|
|
78
|
+
}
|
|
79
|
+
if (options.harnessStreamUrl) {
|
|
80
|
+
args.push('--harness-stream-url', String(options.harnessStreamUrl));
|
|
81
|
+
}
|
|
82
|
+
if (options.harnessControlUrl) {
|
|
83
|
+
args.push('--harness-control-url', String(options.harnessControlUrl));
|
|
84
|
+
}
|
|
85
|
+
if (options.sessionId) {
|
|
86
|
+
args.push('--session-id', String(options.sessionId));
|
|
87
|
+
}
|
|
88
|
+
this.logger.info(chalk.cyan(`Launching V4 Operating Agent from ${v4Path}`));
|
|
89
|
+
const child = spawn(pythonBin, args, {
|
|
90
|
+
cwd: v4Path,
|
|
91
|
+
stdio: 'inherit',
|
|
92
|
+
env: {
|
|
93
|
+
...process.env,
|
|
94
|
+
PYTHONUNBUFFERED: '1',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
98
|
+
child.on('error', reject);
|
|
99
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
100
|
+
});
|
|
101
|
+
if (exitCode !== 0) {
|
|
102
|
+
throw new Error(`V4 agent exited with status ${exitCode}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import { ForkCommand } from './commands/fork.js';
|
|
|
38
38
|
import { CancelCommand } from './commands/cancel.js';
|
|
39
39
|
import { BackgroundCommand } from './commands/background.js';
|
|
40
40
|
import { SecurityCommand } from './commands/security.js';
|
|
41
|
+
import { V4Command } from './commands/v4.js';
|
|
41
42
|
import { Config } from './utils/config.js';
|
|
42
43
|
import { Logger, CH } from './utils/logger.js';
|
|
43
44
|
import chalk from 'chalk';
|
|
@@ -472,8 +473,14 @@ function normalizeCliError(error) {
|
|
|
472
473
|
? extended.response.data.message
|
|
473
474
|
: undefined;
|
|
474
475
|
const rawMessage = responseMessage || extended.message || 'An unexpected CLI error occurred.';
|
|
476
|
+
// Purely local, client-side errors (e.g. "this config path doesn't exist
|
|
477
|
+
// on YOUR machine") can legitimately mention local paths that happen to
|
|
478
|
+
// look like internal server paths (e.g. `/var/www/...`-style directories
|
|
479
|
+
// the user copied/typed themselves). Scrubbing those makes an otherwise
|
|
480
|
+
// actionable message unreadable, so commands that construct their own
|
|
481
|
+
// local filesystem errors can opt out via `skipPathScrub`.
|
|
475
482
|
return {
|
|
476
|
-
message: scrubMessageForUser(rawMessage),
|
|
483
|
+
message: extended.skipPathScrub ? rawMessage : scrubMessageForUser(rawMessage),
|
|
477
484
|
code: extended.code,
|
|
478
485
|
status: extended.status || extended.statusCode || extended.response?.status,
|
|
479
486
|
details: extended.details || extended.response?.data,
|
|
@@ -739,6 +746,37 @@ export async function main(args) {
|
|
|
739
746
|
bridge: options.bridge,
|
|
740
747
|
});
|
|
741
748
|
});
|
|
749
|
+
program
|
|
750
|
+
.command('v4')
|
|
751
|
+
.alias('v4-agent')
|
|
752
|
+
.description('Launch Vigthoria V4 Operating Agent (DeerFlow 2.0 harness)')
|
|
753
|
+
.option('--provider <provider>', 'Provider: cloud | v3_local | byok')
|
|
754
|
+
.option('--api-key <key>', 'BYOK API key for non-interactive mode')
|
|
755
|
+
.option('--base-url <url>', 'Provider base URL override')
|
|
756
|
+
.option('--model <name>', 'Provider model override')
|
|
757
|
+
.option('--transport <mode>', 'Bridge transport: auto | ws | sse', 'auto')
|
|
758
|
+
.option('--harness-stream-url <url>', 'Override harness stream URL')
|
|
759
|
+
.option('--harness-control-url <url>', 'Override harness control URL')
|
|
760
|
+
.option('--session-id <id>', 'Explicit V4 session id')
|
|
761
|
+
.option('--non-interactive', 'Disable interactive menu and use CLI options', false)
|
|
762
|
+
.option('--python <bin>', 'Python binary to use', process.env.VIGTHORIA_V4_PYTHON || 'python3')
|
|
763
|
+
.option('--v4-path <path>', 'V4 agent root path', process.env.VIGTHORIA_V4_AGENT_DIR || '/var/www/V4-Operating-Agent')
|
|
764
|
+
.action(async (options) => {
|
|
765
|
+
const v4 = new V4Command(config, logger);
|
|
766
|
+
await v4.run({
|
|
767
|
+
provider: options.provider,
|
|
768
|
+
apiKey: options.apiKey,
|
|
769
|
+
baseUrl: options.baseUrl,
|
|
770
|
+
model: options.model,
|
|
771
|
+
transport: options.transport,
|
|
772
|
+
harnessStreamUrl: options.harnessStreamUrl,
|
|
773
|
+
harnessControlUrl: options.harnessControlUrl,
|
|
774
|
+
sessionId: options.sessionId,
|
|
775
|
+
nonInteractive: options.nonInteractive,
|
|
776
|
+
python: options.python,
|
|
777
|
+
v4Path: options.v4Path,
|
|
778
|
+
});
|
|
779
|
+
});
|
|
742
780
|
program
|
|
743
781
|
.command('bridge')
|
|
744
782
|
.description('Inspect local DevTools Bridge availability for browser debugging tasks')
|
|
@@ -124,9 +124,17 @@ function minimalHelloWorldHtml(popupMessage = 'Hello World') {
|
|
|
124
124
|
</html>
|
|
125
125
|
`;
|
|
126
126
|
}
|
|
127
|
+
// The Template Service (port 4011) hard-rejects any `vision` payload over
|
|
128
|
+
// 2000 characters with an HTTP 400. Real prompts (especially resumed-session
|
|
129
|
+
// `/retry` context on an existing project) routinely exceed that, which
|
|
130
|
+
// otherwise made this fast-path fail every single time for non-trivial
|
|
131
|
+
// requests instead of ever getting a chance to match. Cap to the server's
|
|
132
|
+
// own limit so template matching still gets a fair shot on long prompts.
|
|
133
|
+
const TEMPLATE_SERVICE_MAX_VISION_CHARS = 2000;
|
|
127
134
|
async function fetchTemplateMatch(api, vision, timeoutMs = 15000) {
|
|
128
135
|
const bases = api.getTemplateServiceBaseUrls();
|
|
129
136
|
let lastError = 'Template Service unreachable';
|
|
137
|
+
const boundedVision = String(vision || '').slice(0, TEMPLATE_SERVICE_MAX_VISION_CHARS);
|
|
130
138
|
for (const base of bases) {
|
|
131
139
|
const controller = new AbortController();
|
|
132
140
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -134,7 +142,7 @@ async function fetchTemplateMatch(api, vision, timeoutMs = 15000) {
|
|
|
134
142
|
const response = await fetch(`${base}/match`, {
|
|
135
143
|
method: 'POST',
|
|
136
144
|
headers: { 'Content-Type': 'application/json' },
|
|
137
|
-
body: JSON.stringify({ vision }),
|
|
145
|
+
body: JSON.stringify({ vision: boundedVision }),
|
|
138
146
|
signal: controller.signal,
|
|
139
147
|
});
|
|
140
148
|
clearTimeout(timer);
|