@controlvector/cv-agent 1.11.0 → 1.12.0
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/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README.md +12 -2
- package/dist/bundle.cjs +17620 -304
- package/dist/bundle.cjs.map +4 -4
- package/dist/commands/agent-git.d.ts +1 -0
- package/dist/commands/agent-git.d.ts.map +1 -1
- package/dist/commands/agent-git.js +26 -3
- package/dist/commands/agent-git.js.map +1 -1
- package/dist/commands/agent.d.ts +49 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +164 -10
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +20 -1
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/git-credential.d.ts +14 -0
- package/dist/commands/git-credential.d.ts.map +1 -0
- package/dist/commands/git-credential.js +53 -0
- package/dist/commands/git-credential.js.map +1 -0
- package/dist/commands/orgs.d.ts +9 -0
- package/dist/commands/orgs.d.ts.map +1 -0
- package/dist/commands/orgs.js +72 -0
- package/dist/commands/orgs.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +30 -20
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/api.d.ts +1 -1
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +4 -2
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/credentials.d.ts +2 -0
- package/dist/utils/credentials.d.ts.map +1 -1
- package/dist/utils/credentials.js.map +1 -1
- package/dist/utils/git-credentials.d.ts +78 -0
- package/dist/utils/git-credentials.d.ts.map +1 -0
- package/dist/utils/git-credentials.js +171 -0
- package/dist/utils/git-credentials.js.map +1 -0
- package/dist/utils/meter.d.ts +59 -0
- package/dist/utils/meter.d.ts.map +1 -0
- package/dist/utils/meter.js +125 -0
- package/dist/utils/meter.js.map +1 -0
- package/dist/utils/profile.d.ts +123 -0
- package/dist/utils/profile.d.ts.map +1 -0
- package/dist/utils/profile.js +266 -0
- package/dist/utils/profile.js.map +1 -0
- package/package.json +17 -7
|
@@ -53,6 +53,7 @@ export declare function buildCompletionPayload(exitCode: number, preState: PreTa
|
|
|
53
53
|
export declare function verifyGitRemote(cwd: string, task: {
|
|
54
54
|
owner?: string;
|
|
55
55
|
repo?: string;
|
|
56
|
+
clone_url?: string;
|
|
56
57
|
}, gitHost: string): {
|
|
57
58
|
remoteName: string;
|
|
58
59
|
remoteUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-git.d.ts","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAO7D;AAMD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,aAAa,CAmGvF;AAMD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAAW,GAClB,qBAAqB,CAsDvB;AAMD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"agent-git.d.ts","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAO7D;AAMD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,aAAa,CAmGvF;AAMD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAAW,GAClB,qBAAqB,CAsDvB;AAMD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3D,OAAO,EAAE,MAAM,GACd;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAyDlD"}
|
|
@@ -186,9 +186,32 @@ function buildCompletionPayload(exitCode, preState, postState, startTime, output
|
|
|
186
186
|
// Git remote verification
|
|
187
187
|
// ============================================================================
|
|
188
188
|
function verifyGitRemote(cwd, task, gitHost) {
|
|
189
|
-
|
|
189
|
+
// Determine the canonical remote URL. Prefer the authoritative clone_url from
|
|
190
|
+
// the task payload: cv-hub already knows the correct URL for its own
|
|
191
|
+
// deployment topology (a subdomain git host like git.hub.controlvector.io, or
|
|
192
|
+
// single-host path-based git like govhub.controlvector.io/git). Only fall back
|
|
193
|
+
// to deriving the URL from owner/repo + the api.->git. host swap when clone_url
|
|
194
|
+
// is absent, for backward compatibility with an older cv-hub. The URL is used
|
|
195
|
+
// verbatim and stays token-free; auth is handled elsewhere.
|
|
196
|
+
let expectedRemote;
|
|
197
|
+
if (task.clone_url) {
|
|
198
|
+
expectedRemote = task.clone_url;
|
|
199
|
+
}
|
|
200
|
+
else if (task.owner && task.repo) {
|
|
201
|
+
expectedRemote = `https://${gitHost}/${task.owner}/${task.repo}.git`;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
190
204
|
return null;
|
|
191
|
-
|
|
205
|
+
}
|
|
206
|
+
// Host used to decide whether an existing origin already points at our git
|
|
207
|
+
// host (correct it in place) versus an unrelated remote (leave it alone; add
|
|
208
|
+
// a separate cv-hub remote). Derived from the chosen URL so it stays correct
|
|
209
|
+
// when clone_url targets a different host than the api.->git. derivation.
|
|
210
|
+
let canonicalHost = gitHost;
|
|
211
|
+
try {
|
|
212
|
+
canonicalHost = new URL(expectedRemote).host;
|
|
213
|
+
}
|
|
214
|
+
catch { /* unparseable URL: keep the derived gitHost */ }
|
|
192
215
|
const currentRemote = gitExec('git remote get-url origin', cwd);
|
|
193
216
|
if (!currentRemote) {
|
|
194
217
|
try {
|
|
@@ -200,7 +223,7 @@ function verifyGitRemote(cwd, task, gitHost) {
|
|
|
200
223
|
if (currentRemote === expectedRemote) {
|
|
201
224
|
return { remoteName: 'origin', remoteUrl: expectedRemote };
|
|
202
225
|
}
|
|
203
|
-
if (currentRemote.includes(
|
|
226
|
+
if (currentRemote.includes(canonicalHost)) {
|
|
204
227
|
try {
|
|
205
228
|
(0, node_child_process_1.execSync)(`git remote set-url origin ${expectedRemote}`, { cwd, timeout: 5000 });
|
|
206
229
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-git.js","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAyDH,0BAMC;AAMD,kDAOC;AAMD,oDAmGC;AAMD,wDA4DC;AAMD,
|
|
1
|
+
{"version":3,"file":"agent-git.js","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -16,9 +16,55 @@
|
|
|
16
16
|
* cva agent --poll-interval 10 # Check every 10 seconds
|
|
17
17
|
*/
|
|
18
18
|
import { Command } from 'commander';
|
|
19
|
+
import { type CVHubCredentials } from '../utils/credentials.js';
|
|
20
|
+
import { type MeterResolution } from '../utils/meter.js';
|
|
21
|
+
import { type LoadedProfile } from '../utils/profile.js';
|
|
19
22
|
export type AuthStatus = 'authenticated' | 'expired' | 'not_configured' | 'api_key_fallback';
|
|
20
23
|
/** Patterns that indicate Claude Code auth failure */
|
|
21
24
|
export declare const AUTH_ERROR_PATTERNS: string[];
|
|
25
|
+
interface AgentState {
|
|
26
|
+
executorId: string;
|
|
27
|
+
currentTaskId: string | null;
|
|
28
|
+
completedCount: number;
|
|
29
|
+
failedCount: number;
|
|
30
|
+
lastPoll: number;
|
|
31
|
+
lastTaskEnd: number;
|
|
32
|
+
running: boolean;
|
|
33
|
+
authStatus: AuthStatus;
|
|
34
|
+
machineName: string;
|
|
35
|
+
meter: MeterResolution;
|
|
36
|
+
denyStreak: number;
|
|
37
|
+
denyCache: Map<string, number>;
|
|
38
|
+
profile?: LoadedProfile;
|
|
39
|
+
}
|
|
40
|
+
interface Task {
|
|
41
|
+
id: string;
|
|
42
|
+
title: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
task_type: string;
|
|
45
|
+
priority: string;
|
|
46
|
+
status: string;
|
|
47
|
+
input?: {
|
|
48
|
+
description?: string;
|
|
49
|
+
context?: string;
|
|
50
|
+
instructions?: string[];
|
|
51
|
+
constraints?: string[];
|
|
52
|
+
};
|
|
53
|
+
repository_id?: string;
|
|
54
|
+
owner?: string;
|
|
55
|
+
repo?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Authoritative clone URL supplied by cv-hub for this task's repository.
|
|
58
|
+
* Correct for whatever deployment topology cv-hub runs (subdomain git host
|
|
59
|
+
* or single-host path-based git), so it is preferred over re-deriving the
|
|
60
|
+
* host from the API URL. Token-free; auth is handled separately.
|
|
61
|
+
*/
|
|
62
|
+
clone_url?: string;
|
|
63
|
+
branch?: string;
|
|
64
|
+
file_paths?: string[];
|
|
65
|
+
timeout_at?: string;
|
|
66
|
+
metadata?: Record<string, unknown>;
|
|
67
|
+
}
|
|
22
68
|
/**
|
|
23
69
|
* Check if a string contains Claude Code auth error patterns.
|
|
24
70
|
* Exported for testing.
|
|
@@ -45,5 +91,8 @@ export declare function getClaudeEnv(): Promise<{
|
|
|
45
91
|
* Build an actionable auth failure message with machine name and fix instructions.
|
|
46
92
|
*/
|
|
47
93
|
export declare function buildAuthFailureMessage(errorString: string, machineName: string, hasApiKeyFallback: boolean): string;
|
|
94
|
+
export declare function reportBudgetDenied(task: Task, state: AgentState, creds: CVHubCredentials, reason: string, reasonCode: string): Promise<void>;
|
|
95
|
+
export declare function budgetGate(task: Task, state: AgentState, creds: CVHubCredentials): Promise<boolean>;
|
|
48
96
|
export declare function agentCommand(): Command;
|
|
97
|
+
export {};
|
|
49
98
|
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/agent.js
CHANGED
|
@@ -58,6 +58,8 @@ exports.containsAuthError = containsAuthError;
|
|
|
58
58
|
exports.checkClaudeAuth = checkClaudeAuth;
|
|
59
59
|
exports.getClaudeEnv = getClaudeEnv;
|
|
60
60
|
exports.buildAuthFailureMessage = buildAuthFailureMessage;
|
|
61
|
+
exports.reportBudgetDenied = reportBudgetDenied;
|
|
62
|
+
exports.budgetGate = budgetGate;
|
|
61
63
|
exports.agentCommand = agentCommand;
|
|
62
64
|
const commander_1 = require("commander");
|
|
63
65
|
const node_child_process_1 = require("node:child_process");
|
|
@@ -74,6 +76,8 @@ const retry_js_1 = require("../utils/retry.js");
|
|
|
74
76
|
const config_js_1 = require("../utils/config.js");
|
|
75
77
|
const git_safety_js_1 = require("./git-safety.js");
|
|
76
78
|
const deploy_manifest_js_1 = require("./deploy-manifest.js");
|
|
79
|
+
const meter_js_1 = require("../utils/meter.js");
|
|
80
|
+
const profile_js_1 = require("../utils/profile.js");
|
|
77
81
|
/** Patterns that indicate Claude Code auth failure */
|
|
78
82
|
exports.AUTH_ERROR_PATTERNS = [
|
|
79
83
|
'Not logged in',
|
|
@@ -165,8 +169,12 @@ function buildAuthFailureMessage(errorString, machineName, hasApiKeyFallback) {
|
|
|
165
169
|
// ============================================================================
|
|
166
170
|
// Claude Code launcher
|
|
167
171
|
// ============================================================================
|
|
168
|
-
function buildClaudePrompt(task) {
|
|
172
|
+
function buildClaudePrompt(task, profile) {
|
|
169
173
|
let prompt = '';
|
|
174
|
+
// Profile system prompt (P14) leads, so it frames every task this session runs.
|
|
175
|
+
if (profile) {
|
|
176
|
+
prompt += `${profile.systemPrompt.trim()}\n\n---\n\n`;
|
|
177
|
+
}
|
|
170
178
|
prompt += `You are executing a task dispatched via CV-Hub.\n\n`;
|
|
171
179
|
prompt += `## Task: ${task.title}\n`;
|
|
172
180
|
prompt += `Task ID: ${task.id}\n`;
|
|
@@ -296,6 +304,7 @@ const MAX_OUTPUT_FINAL_BYTES = 50 * 1024;
|
|
|
296
304
|
/** Interval (in bytes) at which to post progress events with output chunks */
|
|
297
305
|
const OUTPUT_PROGRESS_INTERVAL = 4096;
|
|
298
306
|
async function launchAutoApproveMode(prompt, options) {
|
|
307
|
+
const allowedTools = options.allowedTools ?? ALLOWED_TOOLS;
|
|
299
308
|
// Use a stable session ID so we can --continue if a question needs follow-up
|
|
300
309
|
const sessionId = options.taskId
|
|
301
310
|
? options.taskId.replace(/-/g, '').slice(0, 32).padEnd(32, '0')
|
|
@@ -308,8 +317,8 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
308
317
|
const runOnce = (inputPrompt, isContinue) => {
|
|
309
318
|
return new Promise((resolve, reject) => {
|
|
310
319
|
const args = isContinue
|
|
311
|
-
? ['-p', inputPrompt, '--continue', '--allowedTools', ...
|
|
312
|
-
: ['-p', inputPrompt, '--allowedTools', ...
|
|
320
|
+
? ['-p', inputPrompt, '--continue', '--allowedTools', ...allowedTools]
|
|
321
|
+
: ['-p', inputPrompt, '--allowedTools', ...allowedTools];
|
|
313
322
|
if (sessionId && !isContinue) {
|
|
314
323
|
args.push('--session-id', sessionId);
|
|
315
324
|
}
|
|
@@ -573,6 +582,21 @@ async function launchRelayMode(prompt, options) {
|
|
|
573
582
|
if (match) {
|
|
574
583
|
const promptText = match[0];
|
|
575
584
|
stdoutBuffer = ''; // Reset buffer after match
|
|
585
|
+
// Profile hard gate (P14): a denied tool is refused AT THE RELAY (answer no
|
|
586
|
+
// and skip the human relay entirely), not merely omitted from the prompt.
|
|
587
|
+
if (options.profile) {
|
|
588
|
+
const decision = (0, profile_js_1.relayPermissionDecision)(options.profile.manifest, promptText);
|
|
589
|
+
if (decision.action === 'deny') {
|
|
590
|
+
child.stdin?.write('n\n');
|
|
591
|
+
console.log(chalk_1.default.red(` [relay] Profile '${options.profile.manifest.name}' denies ${decision.tool}: ${decision.reason}`));
|
|
592
|
+
(0, api_js_1.sendTaskLog)(options.creds, options.executorId, options.taskId, 'warn', `Tool denied by profile: ${decision.tool}`, { tool: decision.tool, reason: decision.reason, profile: options.profile.manifest.name }).catch(() => { });
|
|
593
|
+
(0, api_js_1.postTaskEvent)(options.creds, options.taskId, {
|
|
594
|
+
event_type: 'tool_denied_by_profile',
|
|
595
|
+
content: { tool: decision.tool, reason: decision.reason, prompt_text: promptText },
|
|
596
|
+
}).catch(() => { });
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
576
600
|
try {
|
|
577
601
|
await (0, api_js_1.sendTaskLog)(options.creds, options.executorId, options.taskId, 'info', `Permission prompt: ${promptText}`, { prompt_text: promptText });
|
|
578
602
|
const { prompt_id } = await (0, api_js_1.createTaskPrompt)(options.creds, options.executorId, options.taskId, promptText, 'approval', ['y', 'n']);
|
|
@@ -736,6 +760,22 @@ async function handleSelfUpdate(task, state, creds) {
|
|
|
736
760
|
// ============================================================================
|
|
737
761
|
async function runAgent(options) {
|
|
738
762
|
const creds = await (0, credentials_js_1.readCredentials)();
|
|
763
|
+
// Load the agent profile first (P14): a bad profile must fail before we register an
|
|
764
|
+
// executor or poll for work. The profile is a hard gate for the whole session.
|
|
765
|
+
let loadedProfile;
|
|
766
|
+
if (options.profile) {
|
|
767
|
+
const hostVersion = typeof __CVA_VERSION__ !== 'undefined' ? __CVA_VERSION__ : '0.0.0';
|
|
768
|
+
try {
|
|
769
|
+
loadedProfile = (0, profile_js_1.loadProfile)(options.profile, { hostVersion });
|
|
770
|
+
}
|
|
771
|
+
catch (err) {
|
|
772
|
+
console.log();
|
|
773
|
+
console.log(chalk_1.default.red('Profile failed to load:') + ' ' + err.message);
|
|
774
|
+
console.log();
|
|
775
|
+
process.exit(1);
|
|
776
|
+
}
|
|
777
|
+
console.log(chalk_1.default.cyan(` Profile: ${loadedProfile.manifest.name} ${loadedProfile.manifest.version} (${loadedProfile.manifest.task_types.length} task types, ${loadedProfile.manifest.tools.allow.length} tools allowed)`));
|
|
778
|
+
}
|
|
739
779
|
if (!creds.CV_HUB_API) {
|
|
740
780
|
creds.CV_HUB_API = 'https://api.hub.controlvector.io';
|
|
741
781
|
}
|
|
@@ -946,6 +986,10 @@ async function runAgent(options) {
|
|
|
946
986
|
// Priority: CLI flags > workspace .cva/agent.json > global config
|
|
947
987
|
const wsConfig = await (0, config_js_1.readWorkspaceConfig)(workingDir);
|
|
948
988
|
const globalConfig = await (0, config_js_1.readConfig)();
|
|
989
|
+
// Inject meter config into spawn env so cv-code can attribute tokens to this executor.
|
|
990
|
+
// task_id is added per-task inside executeTask since it changes each run.
|
|
991
|
+
const meter = (0, meter_js_1.resolveMeterConfig)(globalConfig, process.env);
|
|
992
|
+
(0, meter_js_1.applyMeterEnv)(claudeEnv, meter);
|
|
949
993
|
const role = (options.role || wsConfig.role || globalConfig.role || 'development');
|
|
950
994
|
const dispatchGuard = (options.dispatchGuard || wsConfig.dispatch_guard || globalConfig.dispatch_guard || 'open');
|
|
951
995
|
const tags = (options.tags?.split(',') || wsConfig.tags || globalConfig.tags)?.map((t) => t.trim()).filter(Boolean);
|
|
@@ -1004,6 +1048,8 @@ async function runAgent(options) {
|
|
|
1004
1048
|
console.log(` Dir: ${chalk_1.default.gray(workingDir)}`);
|
|
1005
1049
|
console.log(` Polling: every ${options.pollInterval}s`);
|
|
1006
1050
|
console.log(` Mode: ${chalk_1.default.cyan(mode)}`);
|
|
1051
|
+
if (meter.url)
|
|
1052
|
+
console.log(` Meter: ${chalk_1.default.gray(meter.url)}${meter.enforce !== 'off' ? chalk_1.default.yellow(` (enforce: ${meter.enforce})`) : ''}`);
|
|
1007
1053
|
console.log(` Ctrl+C to stop`);
|
|
1008
1054
|
console.log();
|
|
1009
1055
|
console.log(chalk_1.default.cyan('Listening for tasks...'));
|
|
@@ -1018,7 +1064,15 @@ async function runAgent(options) {
|
|
|
1018
1064
|
running: true,
|
|
1019
1065
|
authStatus: currentAuthStatus,
|
|
1020
1066
|
machineName,
|
|
1067
|
+
meter,
|
|
1068
|
+
denyStreak: 0,
|
|
1069
|
+
denyCache: new Map(),
|
|
1070
|
+
profile: loadedProfile,
|
|
1021
1071
|
};
|
|
1072
|
+
// If enforcement is enabled we need a meter URL to call; warn loudly if misconfigured.
|
|
1073
|
+
if (meter.enforce !== 'off' && !meter.url) {
|
|
1074
|
+
console.log(chalk_1.default.yellow('!') + ` meter_enforce='${meter.enforce}' but no meter_url set — budget gate inert.`);
|
|
1075
|
+
}
|
|
1022
1076
|
installSignalHandlers(() => state, async () => {
|
|
1023
1077
|
state.running = false;
|
|
1024
1078
|
await (0, api_js_1.markOffline)(creds, state.executorId);
|
|
@@ -1035,7 +1089,7 @@ async function runAgent(options) {
|
|
|
1035
1089
|
}
|
|
1036
1090
|
else {
|
|
1037
1091
|
// Still expired — heartbeat but don't poll for tasks
|
|
1038
|
-
(0, api_js_1.sendHeartbeat)(creds, state.executorId, undefined, undefined, state.authStatus).catch(() => { });
|
|
1092
|
+
(0, api_js_1.sendHeartbeat)(creds, state.executorId, undefined, undefined, state.authStatus, state.meter.enforce).catch(() => { });
|
|
1039
1093
|
await new Promise(r => setTimeout(r, pollInterval));
|
|
1040
1094
|
continue;
|
|
1041
1095
|
}
|
|
@@ -1052,8 +1106,62 @@ async function runAgent(options) {
|
|
|
1052
1106
|
catch (err) {
|
|
1053
1107
|
console.log(`\n${chalk_1.default.red('!')} Error: ${err.message}`);
|
|
1054
1108
|
}
|
|
1055
|
-
|
|
1109
|
+
// Exponential backoff after consecutive budget denials so a fully-over-budget scope isn't
|
|
1110
|
+
// hammered at poll cadence (capped at 5 min); a single allow/warn resets denyStreak to 0.
|
|
1111
|
+
await new Promise(r => setTimeout(r, (0, meter_js_1.pollBackoffMs)(pollInterval, state.denyStreak)));
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
// Report a budget denial to CV-Hub: a machine-readable failTask reason (so the planner
|
|
1115
|
+
// doesn't auto-retry into the wall) + a budget_denied event. Increments failedCount.
|
|
1116
|
+
async function reportBudgetDenied(task, state, creds, reason, reasonCode) {
|
|
1117
|
+
console.log(`⛔ ${chalk_1.default.bold.red('BUDGET DENIED')} — ${reason}`);
|
|
1118
|
+
(0, api_js_1.postTaskEvent)(creds, task.id, {
|
|
1119
|
+
event_type: 'budget_denied',
|
|
1120
|
+
content: { reason, reason_code: reasonCode, deny_streak: state.denyStreak },
|
|
1121
|
+
}).catch(() => { });
|
|
1122
|
+
await (0, retry_js_1.withRetry)(() => (0, api_js_1.failTask)(creds, state.executorId, task.id, `BUDGET_EXCEEDED: ${reason}`), 'Report budget denial').catch(() => { });
|
|
1123
|
+
state.failedCount++;
|
|
1124
|
+
}
|
|
1125
|
+
// Pre-dispatch budget gate (cv-meter Phase 4). Runs BEFORE any engine is spawned, so it is
|
|
1126
|
+
// engine-agnostic (Claude Code today, cv-code per #11). Returns true to proceed, false if the
|
|
1127
|
+
// task was budget-denied (already reported). org is derived from the task's repo owner.
|
|
1128
|
+
async function budgetGate(task, state, creds) {
|
|
1129
|
+
const meter = state.meter;
|
|
1130
|
+
if (meter.enforce === 'off' || !meter.url)
|
|
1131
|
+
return true;
|
|
1132
|
+
// org = the task's owning tenant (repo owner slug); config override as a fallback.
|
|
1133
|
+
const org = task.owner ?? meter.org;
|
|
1134
|
+
if (!org)
|
|
1135
|
+
return true; // no org to tenant-scope the check → can't enforce; proceed
|
|
1136
|
+
const scopes = {
|
|
1137
|
+
org,
|
|
1138
|
+
...(task.owner && task.repo ? { project: `${task.owner}/${task.repo}` } : {}),
|
|
1139
|
+
task: task.id,
|
|
1140
|
+
};
|
|
1141
|
+
const now = Date.now();
|
|
1142
|
+
// Negative-cache: a scope seen denied recently short-circuits without a round-trip.
|
|
1143
|
+
if ((0, meter_js_1.checkDenyCache)(state.denyCache, scopes, now)) {
|
|
1144
|
+
state.denyStreak++;
|
|
1145
|
+
await reportBudgetDenied(task, state, creds, 'budget exceeded (cached)', 'HARD_DENY');
|
|
1146
|
+
return false;
|
|
1056
1147
|
}
|
|
1148
|
+
const verdict = await (0, meter_js_1.meterCheck)(meter, org, scopes);
|
|
1149
|
+
const action = verdict ? verdict.action : (0, meter_js_1.resolveErrorAction)(meter.enforce);
|
|
1150
|
+
if (action === 'deny') {
|
|
1151
|
+
state.denyStreak++;
|
|
1152
|
+
(0, meter_js_1.noteDeny)(state.denyCache, verdict?.governingKey, now);
|
|
1153
|
+
await reportBudgetDenied(task, state, creds, verdict?.reason || 'meter check failed (fail_closed)', verdict?.reasonCode ?? 'HARD_DENY');
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
state.denyStreak = 0; // any allow/warn (incl. fail_open, NO_BUDGET) resets backoff
|
|
1157
|
+
if (action === 'warn' && verdict) {
|
|
1158
|
+
(0, api_js_1.sendTaskLog)(creds, state.executorId, task.id, 'info', `Budget warning: ${verdict.reason}`, { reason_code: verdict.reasonCode }).catch(() => { });
|
|
1159
|
+
(0, api_js_1.postTaskEvent)(creds, task.id, {
|
|
1160
|
+
event_type: 'budget_warning',
|
|
1161
|
+
content: { reason: verdict.reason, reason_code: verdict.reasonCode, governing: verdict.governingKey, scopes },
|
|
1162
|
+
}).catch(() => { });
|
|
1163
|
+
}
|
|
1164
|
+
return true;
|
|
1057
1165
|
}
|
|
1058
1166
|
async function executeTask(task, state, creds, options, claudeEnv) {
|
|
1059
1167
|
const startTime = Date.now();
|
|
@@ -1063,6 +1171,31 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1063
1171
|
await handleSelfUpdate(task, state, creds);
|
|
1064
1172
|
return;
|
|
1065
1173
|
}
|
|
1174
|
+
// Profile task-type gate (P14): a task whose type is outside the profile's task_types
|
|
1175
|
+
// is refused here, before any model call, with a structured reason.
|
|
1176
|
+
if (state.profile) {
|
|
1177
|
+
const verdict = (0, profile_js_1.checkTaskType)(state.profile.manifest, task.task_type);
|
|
1178
|
+
if (!verdict.allowed) {
|
|
1179
|
+
console.log(chalk_1.default.red(` [profile] refused ${task.id}: ${verdict.reason}`));
|
|
1180
|
+
(0, api_js_1.postTaskEvent)(creds, task.id, {
|
|
1181
|
+
event_type: 'profile_refused',
|
|
1182
|
+
content: { reason: verdict.reason, profile: state.profile.manifest.name, task_type: task.task_type },
|
|
1183
|
+
}).catch(() => { });
|
|
1184
|
+
try {
|
|
1185
|
+
await (0, api_js_1.failTask)(creds, state.executorId, task.id, `PROFILE_REFUSED: ${verdict.reason}`);
|
|
1186
|
+
}
|
|
1187
|
+
catch { }
|
|
1188
|
+
state.currentTaskId = null;
|
|
1189
|
+
state.lastTaskEnd = Date.now();
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// Pre-dispatch budget gate — engine-agnostic, before startTask/spawn (cv-meter Phase 4 / #12).
|
|
1194
|
+
if (!(await budgetGate(task, state, creds))) {
|
|
1195
|
+
state.currentTaskId = null;
|
|
1196
|
+
state.lastTaskEnd = Date.now();
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1066
1199
|
// Clear status line
|
|
1067
1200
|
process.stdout.write('\r\x1b[K');
|
|
1068
1201
|
// Task received
|
|
@@ -1090,7 +1223,7 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1090
1223
|
try {
|
|
1091
1224
|
const elapsed = (0, display_js_1.formatDuration)(Date.now() - startTime);
|
|
1092
1225
|
(0, display_js_1.setTerminalTitle)(`cva: ${task.title} (${elapsed})`);
|
|
1093
|
-
await (0, api_js_1.sendHeartbeat)(creds, state.executorId, task.id, `Claude Code running (${elapsed} elapsed)`, state.authStatus);
|
|
1226
|
+
await (0, api_js_1.sendHeartbeat)(creds, state.executorId, task.id, `Claude Code running (${elapsed} elapsed)`, state.authStatus, state.meter.enforce);
|
|
1094
1227
|
}
|
|
1095
1228
|
catch { }
|
|
1096
1229
|
}, 30_000);
|
|
@@ -1115,7 +1248,7 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1115
1248
|
console.log(chalk_1.default.gray(` Git remote: ${remoteInfo.remoteName} -> ${remoteInfo.remoteUrl}`));
|
|
1116
1249
|
}
|
|
1117
1250
|
// Build prompt and launch
|
|
1118
|
-
const prompt = buildClaudePrompt(task);
|
|
1251
|
+
const prompt = buildClaudePrompt(task, state.profile);
|
|
1119
1252
|
// Persistent event queue — retries failed output events, spills to disk on
|
|
1120
1253
|
// crash, assigns monotonic sequence numbers. One queue per task; spill file
|
|
1121
1254
|
// lives in OS tmp keyed by task ID and is cleaned up on successful flush.
|
|
@@ -1129,11 +1262,29 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1129
1262
|
await eventQueue.loadSpill();
|
|
1130
1263
|
try {
|
|
1131
1264
|
const mode = options.autoApprove ? 'auto-approve' : 'relay';
|
|
1265
|
+
// Under a profile, auto-approve is constrained to the profile allowlist (deny wins).
|
|
1266
|
+
const effectiveAllowedTools = state.profile ? (0, profile_js_1.allowedToolsList)(state.profile.manifest) : ALLOWED_TOOLS;
|
|
1132
1267
|
console.log(`🚀 ${chalk_1.default.bold.green('RUNNING')} — Launching Claude Code (${mode})...`);
|
|
1133
1268
|
if (options.autoApprove) {
|
|
1134
|
-
console.log(chalk_1.default.gray(' Allowed tools: ') +
|
|
1269
|
+
console.log(chalk_1.default.gray(' Allowed tools: ') + effectiveAllowedTools.join(', '));
|
|
1135
1270
|
}
|
|
1136
1271
|
console.log(chalk_1.default.gray('-'.repeat(60)));
|
|
1272
|
+
// Per-task spawn env: clone the base env and stamp CVMETER_TASK_ID so cv-code
|
|
1273
|
+
// can attribute every token event to this specific task in cv-meter.
|
|
1274
|
+
const taskEnv = (0, meter_js_1.stampTaskEnv)(claudeEnv ?? process.env, task.id);
|
|
1275
|
+
if (taskEnv.CVMETER_URL) {
|
|
1276
|
+
console.log(chalk_1.default.gray(` [meter] task_id=${task.id} → ${taskEnv.CVMETER_URL}`));
|
|
1277
|
+
}
|
|
1278
|
+
// Scope ceiling (P14): the profile's credential_scopes is a maximum. Strip any
|
|
1279
|
+
// broader credential the environment offers before it reaches the spawned engine.
|
|
1280
|
+
if (state.profile) {
|
|
1281
|
+
const offered = (taskEnv.CVA_CREDENTIAL_SCOPES ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
1282
|
+
const capped = (0, profile_js_1.capScopes)(state.profile.manifest.credential_scopes, offered);
|
|
1283
|
+
taskEnv.CVA_CREDENTIAL_SCOPES = capped.join(',');
|
|
1284
|
+
if (offered.length !== capped.length) {
|
|
1285
|
+
console.log(chalk_1.default.yellow(` [profile] scope ceiling stripped ${offered.length - capped.length} scope(s) beyond ${state.profile.manifest.name}`));
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1137
1288
|
let result;
|
|
1138
1289
|
if (options.autoApprove) {
|
|
1139
1290
|
result = await launchAutoApproveMode(prompt, {
|
|
@@ -1141,9 +1292,10 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1141
1292
|
creds,
|
|
1142
1293
|
taskId: task.id,
|
|
1143
1294
|
executorId: state.executorId,
|
|
1144
|
-
spawnEnv:
|
|
1295
|
+
spawnEnv: taskEnv,
|
|
1145
1296
|
machineName: state.machineName,
|
|
1146
1297
|
eventQueue,
|
|
1298
|
+
allowedTools: effectiveAllowedTools,
|
|
1147
1299
|
});
|
|
1148
1300
|
}
|
|
1149
1301
|
else {
|
|
@@ -1152,9 +1304,10 @@ async function executeTask(task, state, creds, options, claudeEnv) {
|
|
|
1152
1304
|
creds,
|
|
1153
1305
|
executorId: state.executorId,
|
|
1154
1306
|
taskId: task.id,
|
|
1155
|
-
spawnEnv:
|
|
1307
|
+
spawnEnv: taskEnv,
|
|
1156
1308
|
machineName: state.machineName,
|
|
1157
1309
|
eventQueue,
|
|
1310
|
+
profile: state.profile,
|
|
1158
1311
|
});
|
|
1159
1312
|
}
|
|
1160
1313
|
console.log(chalk_1.default.gray('\n' + '-'.repeat(60)));
|
|
@@ -1338,6 +1491,7 @@ function agentCommand() {
|
|
|
1338
1491
|
cmd.option('--tags <tags>', 'Comma-separated tags for this executor');
|
|
1339
1492
|
cmd.option('--owner-project <project>', 'Project this executor belongs to');
|
|
1340
1493
|
cmd.option('--org <slug>', 'Organization slug (auto-detected from repo owner if omitted)');
|
|
1494
|
+
cmd.option('--profile <spec>', 'Agent profile to load and enforce: a path or @scope/pkg (P14)');
|
|
1341
1495
|
cmd.action(async (opts) => {
|
|
1342
1496
|
await runAgent(opts);
|
|
1343
1497
|
});
|