@controlvector/cv-agent 1.11.1 → 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 +17599 -335
- 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/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 +2 -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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* cv-meter token-metering plumbing.
|
|
4
|
+
*
|
|
5
|
+
* Pure helpers (no I/O) so the env wiring is unit-testable. The agent reads
|
|
6
|
+
* meter config once at startup and injects it into every Claude Code spawn;
|
|
7
|
+
* cv-code (the producer) reads CVMETER_* from that env and emits usage events.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.resolveMeterConfig = resolveMeterConfig;
|
|
11
|
+
exports.mapReasonToAction = mapReasonToAction;
|
|
12
|
+
exports.resolveErrorAction = resolveErrorAction;
|
|
13
|
+
exports.pollBackoffMs = pollBackoffMs;
|
|
14
|
+
exports.noteDeny = noteDeny;
|
|
15
|
+
exports.checkDenyCache = checkDenyCache;
|
|
16
|
+
exports.meterCheck = meterCheck;
|
|
17
|
+
exports.applyMeterEnv = applyMeterEnv;
|
|
18
|
+
exports.stampTaskEnv = stampTaskEnv;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve meter config from agent config + process env.
|
|
21
|
+
* Config file wins over env (explicit config beats ambient environment).
|
|
22
|
+
*/
|
|
23
|
+
function resolveMeterConfig(config, env) {
|
|
24
|
+
return {
|
|
25
|
+
url: config.meter_url ?? env.CVMETER_URL,
|
|
26
|
+
token: config.meter_token ?? env.CVMETER_TOKEN,
|
|
27
|
+
enforce: (config.meter_enforce ?? env.CVMETER_ENFORCE ?? 'off'),
|
|
28
|
+
org: config.meter_org ?? env.CVMETER_ORG,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Pure: map the server's reason_code to a gate action. NO_BUDGET/OK ⇒ allow.
|
|
32
|
+
function mapReasonToAction(code) {
|
|
33
|
+
return code === 'HARD_DENY' ? 'deny' : code === 'SOFT_WARN' ? 'warn' : 'allow';
|
|
34
|
+
}
|
|
35
|
+
// Pure: on a check error/timeout, fail-open (allow) unless configured fail_closed (deny).
|
|
36
|
+
function resolveErrorAction(enforce) {
|
|
37
|
+
return enforce === 'fail_closed' ? 'deny' : 'allow';
|
|
38
|
+
}
|
|
39
|
+
// Pure: poll sleep with exponential backoff after consecutive budget denials (cap 5 min).
|
|
40
|
+
// denyStreak 0 ⇒ base interval; resets are the caller's job (any allow/warn sets streak 0).
|
|
41
|
+
function pollBackoffMs(baseIntervalMs, denyStreak, capMs = 5 * 60_000) {
|
|
42
|
+
if (denyStreak <= 0)
|
|
43
|
+
return baseIntervalMs;
|
|
44
|
+
return Math.min(baseIntervalMs * 2 ** Math.min(denyStreak - 1, 6), capMs);
|
|
45
|
+
}
|
|
46
|
+
// Pure: negative-cache of denied scope keys → expiry, so a burst of same-scope tasks
|
|
47
|
+
// doesn't fire N /v1/check calls for a scope already known over-cap.
|
|
48
|
+
function noteDeny(cache, governingKey, now, ttlMs = 90_000) {
|
|
49
|
+
if (governingKey)
|
|
50
|
+
cache.set(governingKey, now + ttlMs);
|
|
51
|
+
}
|
|
52
|
+
function checkDenyCache(cache, scopes, now) {
|
|
53
|
+
for (const [k, v] of Object.entries(scopes)) {
|
|
54
|
+
if (!v)
|
|
55
|
+
continue;
|
|
56
|
+
const until = cache.get(`${k}:${v}`);
|
|
57
|
+
if (until && until > now)
|
|
58
|
+
return true;
|
|
59
|
+
if (until && until <= now)
|
|
60
|
+
cache.delete(`${k}:${v}`); // lazy eviction
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Call cv-meter POST /v1/check. Returns the mapped server verdict on HTTP 200, or
|
|
66
|
+
* null on any error/timeout/non-200 (caller applies resolveErrorAction). Never throws.
|
|
67
|
+
* ~1s target timeout, 3s hard ceiling. Side-effect-free ⇒ safe to skip/retry.
|
|
68
|
+
*/
|
|
69
|
+
async function meterCheck(meter, org, scopes, timeoutMs = 1000) {
|
|
70
|
+
if (!meter.url)
|
|
71
|
+
return null;
|
|
72
|
+
const ctl = new AbortController();
|
|
73
|
+
const timer = setTimeout(() => ctl.abort(), Math.min(timeoutMs, 3000));
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(`${meter.url.replace(/\/$/, '')}/v1/check`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json', ...(meter.token ? { Authorization: `Bearer ${meter.token}` } : {}) },
|
|
78
|
+
body: JSON.stringify({ org, scopes }),
|
|
79
|
+
signal: ctl.signal,
|
|
80
|
+
});
|
|
81
|
+
if (!res.ok)
|
|
82
|
+
return null; // 400/401/403/5xx are errors, not budget denials
|
|
83
|
+
const body = await res.json();
|
|
84
|
+
const g = body.governing;
|
|
85
|
+
return {
|
|
86
|
+
action: mapReasonToAction(body.reason_code),
|
|
87
|
+
reasonCode: body.reason_code,
|
|
88
|
+
reason: body.reason ?? '',
|
|
89
|
+
governingKey: g ? `${g.scope}:${g.scope_id}` : undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null; // timeout / network / abort
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Inject resolved meter config into a spawn env (mutates in place).
|
|
101
|
+
* The token is only set when a URL is present — a token without an endpoint is
|
|
102
|
+
* useless and we don't want to leak it into a child that won't meter.
|
|
103
|
+
*/
|
|
104
|
+
function applyMeterEnv(targetEnv, meter) {
|
|
105
|
+
if (!meter.url)
|
|
106
|
+
return;
|
|
107
|
+
targetEnv.CVMETER_URL = meter.url;
|
|
108
|
+
if (meter.token)
|
|
109
|
+
targetEnv.CVMETER_TOKEN = meter.token;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Clone a base spawn env and stamp the per-task CVMETER_TASK_ID.
|
|
113
|
+
*
|
|
114
|
+
* Returns a fresh object so the daemon's base env is never mutated — this is
|
|
115
|
+
* what prevents one task's id from leaking into the next task's events. The id
|
|
116
|
+
* is only stamped when metering is active (CVMETER_URL present in the base env).
|
|
117
|
+
*/
|
|
118
|
+
function stampTaskEnv(baseEnv, taskId) {
|
|
119
|
+
const taskEnv = { ...baseEnv };
|
|
120
|
+
if (taskEnv.CVMETER_URL) {
|
|
121
|
+
taskEnv.CVMETER_TASK_ID = taskId;
|
|
122
|
+
}
|
|
123
|
+
return taskEnv;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=meter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.js","sourceRoot":"","sources":["../../src/utils/meter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent profiles (P14). A profile is data, not a fork: a manifest + system prompt +
|
|
3
|
+
* tool policy that constrains what a cva session can do. It is loaded at start and
|
|
4
|
+
* enforced as a hard gate, so a profiled session cannot exceed its manifest no matter
|
|
5
|
+
* what the model attempts. cv-deploy (P15) is the first profile.
|
|
6
|
+
*
|
|
7
|
+
* This module is pure and self-contained: the manifest schema, the loader, and the
|
|
8
|
+
* enforcement decisions (tool policy, task-type routing, scope ceiling, base semver
|
|
9
|
+
* gate) are all here as testable functions. agent.ts wires them into the run loop and
|
|
10
|
+
* the permission relay; the frozen contract other profiles author against is SPEC.md.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
/**
|
|
14
|
+
* The profile manifest. `.strict()` at every level makes an unknown key an ERROR, not
|
|
15
|
+
* a warning (P14 item 2): a typo or a key from a newer schema fails the load loudly
|
|
16
|
+
* rather than being silently ignored.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ProfileManifestSchema: z.ZodObject<{
|
|
19
|
+
name: z.ZodString;
|
|
20
|
+
version: z.ZodString;
|
|
21
|
+
base: z.ZodString;
|
|
22
|
+
system_prompt: z.ZodString;
|
|
23
|
+
tools: z.ZodObject<{
|
|
24
|
+
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
25
|
+
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
task_types: z.ZodArray<z.ZodString>;
|
|
28
|
+
credential_scopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
29
|
+
limits: z.ZodDefault<z.ZodObject<{
|
|
30
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
max_tool_calls: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
timeout_s: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
}, z.core.$strict>>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
export type ProfileManifest = z.infer<typeof ProfileManifestSchema>;
|
|
36
|
+
export declare class ProfileError extends Error {
|
|
37
|
+
readonly detail: Record<string, unknown>;
|
|
38
|
+
constructor(message: string, detail?: Record<string, unknown>);
|
|
39
|
+
}
|
|
40
|
+
/** Validate an already-parsed object as a manifest. Throws ProfileError on any issue. */
|
|
41
|
+
export declare function validateManifest(input: unknown): ProfileManifest;
|
|
42
|
+
/**
|
|
43
|
+
* Check the manifest `base` constraint against the running host. The grammar is
|
|
44
|
+
* frozen (SPEC): `[<host-name>] <op> <version>`, op one of >= > <= < =, version a
|
|
45
|
+
* bare `major.minor.patch`; a bare version with no op means `=`. A host-name token,
|
|
46
|
+
* when present, must match hostName (a profile for a different host is refused).
|
|
47
|
+
*/
|
|
48
|
+
export declare function checkBaseConstraint(base: string, hostName: string, hostVersion: string): {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
reason?: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A tool spec's base name. Claude Code tools appear as "Bash" or scoped like
|
|
54
|
+
* "Bash(git*)"; policy entries may be "Bash", "Bash(*)", or "Bash(git*)". Policy is
|
|
55
|
+
* evaluated by base name so an allow/deny of "Bash" covers every Bash invocation.
|
|
56
|
+
*/
|
|
57
|
+
export declare function toolBaseName(spec: string): string;
|
|
58
|
+
export interface ToolDecision {
|
|
59
|
+
allowed: boolean;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The tool-policy decision: a deny-list entry wins over an allow-list entry, and
|
|
64
|
+
* absence from the allow-list means denied. This is the hard gate the relay and the
|
|
65
|
+
* auto-approve allowlist are both built from.
|
|
66
|
+
*/
|
|
67
|
+
export declare function evaluateTool(manifest: ProfileManifest, tool: string): ToolDecision;
|
|
68
|
+
/** The `--allowedTools` list for auto-approve mode: allow entries not shadowed by a deny. */
|
|
69
|
+
export declare function allowedToolsList(manifest: ProfileManifest): string[];
|
|
70
|
+
/** Pull the tool name out of a Claude Code permission prompt, or null if not present. */
|
|
71
|
+
export declare function extractToolFromPrompt(promptText: string): string | null;
|
|
72
|
+
/**
|
|
73
|
+
* The relay decision for a permission prompt under a profile. A tool the profile
|
|
74
|
+
* denies is refused AT THE RELAY (action "deny" -> the relay answers no); an allowed
|
|
75
|
+
* (or unidentifiable) tool falls through to the normal human/CV-Hub relay. The profile
|
|
76
|
+
* only hard-denies; it never auto-approves what the operator would otherwise gate.
|
|
77
|
+
*/
|
|
78
|
+
export declare function relayPermissionDecision(manifest: ProfileManifest, promptText: string): {
|
|
79
|
+
action: 'deny' | 'relay';
|
|
80
|
+
tool?: string;
|
|
81
|
+
reason?: string;
|
|
82
|
+
};
|
|
83
|
+
/** A dispatched task whose type is outside `task_types` is refused (before any model call). */
|
|
84
|
+
export declare function checkTaskType(manifest: ProfileManifest, taskType: string): ToolDecision;
|
|
85
|
+
/**
|
|
86
|
+
* Apply the credential-scope ceiling: keep only offered scopes that the profile
|
|
87
|
+
* permits, dropping anything broader. The profile's credential_scopes is a MAXIMUM,
|
|
88
|
+
* so this is the intersection (offered that is within the ceiling).
|
|
89
|
+
*/
|
|
90
|
+
export declare function capScopes(ceiling: string[], offered: string[]): string[];
|
|
91
|
+
export interface LoadedProfile {
|
|
92
|
+
manifest: ProfileManifest;
|
|
93
|
+
systemPrompt: string;
|
|
94
|
+
/** Directory the manifest was loaded from (system_prompt resolves against it). */
|
|
95
|
+
dir: string;
|
|
96
|
+
/** The spec the caller passed (a path or an @scope/pkg). */
|
|
97
|
+
source: string;
|
|
98
|
+
}
|
|
99
|
+
export interface LoadProfileOptions {
|
|
100
|
+
/** Running host version, e.g. the cva version, for the base gate. */
|
|
101
|
+
hostVersion: string;
|
|
102
|
+
/** Host binary name the base constraint may name. Defaults to "cv-agent". */
|
|
103
|
+
hostName?: string;
|
|
104
|
+
/** Injectable file reader (tests). Defaults to fs.readFileSync utf8. */
|
|
105
|
+
readFile?: (path: string) => string;
|
|
106
|
+
/** Injectable package resolver for @scope/pkg specs (tests). Defaults to require.resolve. */
|
|
107
|
+
resolvePackage?: (spec: string) => string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve a profile spec to a manifest file path. A spec beginning with "@" (or any
|
|
111
|
+
* bare package name) is resolved as a package whose root holds profile.yaml; anything
|
|
112
|
+
* else is a filesystem path (a directory implies <dir>/profile.yaml, a file is used
|
|
113
|
+
* as-is).
|
|
114
|
+
*/
|
|
115
|
+
export declare function resolveProfileSpec(spec: string, opts: LoadProfileOptions): string;
|
|
116
|
+
/**
|
|
117
|
+
* Load and validate a profile: resolve the spec, parse + strictly validate the
|
|
118
|
+
* manifest, gate the host version against `base`, then load the system prompt. Every
|
|
119
|
+
* failure is a ProfileError with a specific reason (P14: unknown keys, bad semver, and
|
|
120
|
+
* a failed base gate are all hard errors).
|
|
121
|
+
*/
|
|
122
|
+
export declare function loadProfile(spec: string, opts: LoadProfileOptions): LoadedProfile;
|
|
123
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/utils/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuBxB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;kBAcvB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,qBAAa,YAAa,SAAQ,KAAK;IAGnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBADxC,OAAO,EAAE,MAAM,EACN,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAKhD;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAOhE;AAsBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAuBlC;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAOlF;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,EAAE,CAGpE;AAED,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGvE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,eAAe,EACzB,UAAU,EAAE,MAAM,GACjB;IAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAM9D;AAMD,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAMvF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAGxE;AAMD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3C;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAOjF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAyCjF"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent profiles (P14). A profile is data, not a fork: a manifest + system prompt +
|
|
4
|
+
* tool policy that constrains what a cva session can do. It is loaded at start and
|
|
5
|
+
* enforced as a hard gate, so a profiled session cannot exceed its manifest no matter
|
|
6
|
+
* what the model attempts. cv-deploy (P15) is the first profile.
|
|
7
|
+
*
|
|
8
|
+
* This module is pure and self-contained: the manifest schema, the loader, and the
|
|
9
|
+
* enforcement decisions (tool policy, task-type routing, scope ceiling, base semver
|
|
10
|
+
* gate) are all here as testable functions. agent.ts wires them into the run loop and
|
|
11
|
+
* the permission relay; the frozen contract other profiles author against is SPEC.md.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.ProfileError = exports.ProfileManifestSchema = void 0;
|
|
15
|
+
exports.validateManifest = validateManifest;
|
|
16
|
+
exports.checkBaseConstraint = checkBaseConstraint;
|
|
17
|
+
exports.toolBaseName = toolBaseName;
|
|
18
|
+
exports.evaluateTool = evaluateTool;
|
|
19
|
+
exports.allowedToolsList = allowedToolsList;
|
|
20
|
+
exports.extractToolFromPrompt = extractToolFromPrompt;
|
|
21
|
+
exports.relayPermissionDecision = relayPermissionDecision;
|
|
22
|
+
exports.checkTaskType = checkTaskType;
|
|
23
|
+
exports.capScopes = capScopes;
|
|
24
|
+
exports.resolveProfileSpec = resolveProfileSpec;
|
|
25
|
+
exports.loadProfile = loadProfile;
|
|
26
|
+
const node_fs_1 = require("node:fs");
|
|
27
|
+
const node_path_1 = require("node:path");
|
|
28
|
+
const js_yaml_1 = require("js-yaml");
|
|
29
|
+
const zod_1 = require("zod");
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Manifest schema (profile.yaml)
|
|
32
|
+
// ============================================================================
|
|
33
|
+
const ToolsSchema = zod_1.z
|
|
34
|
+
.object({
|
|
35
|
+
// Explicit allowlist. Absence of a tool here means denied.
|
|
36
|
+
allow: zod_1.z.array(zod_1.z.string().min(1)).default([]),
|
|
37
|
+
// Redundant hard-denies for auditability; a deny wins over an allow.
|
|
38
|
+
deny: zod_1.z.array(zod_1.z.string().min(1)).default([]),
|
|
39
|
+
})
|
|
40
|
+
.strict();
|
|
41
|
+
const LimitsSchema = zod_1.z
|
|
42
|
+
.object({
|
|
43
|
+
max_turns: zod_1.z.number().int().positive().optional(),
|
|
44
|
+
max_tool_calls: zod_1.z.number().int().positive().optional(),
|
|
45
|
+
timeout_s: zod_1.z.number().int().positive().optional(),
|
|
46
|
+
})
|
|
47
|
+
.strict();
|
|
48
|
+
/**
|
|
49
|
+
* The profile manifest. `.strict()` at every level makes an unknown key an ERROR, not
|
|
50
|
+
* a warning (P14 item 2): a typo or a key from a newer schema fails the load loudly
|
|
51
|
+
* rather than being silently ignored.
|
|
52
|
+
*/
|
|
53
|
+
exports.ProfileManifestSchema = zod_1.z
|
|
54
|
+
.object({
|
|
55
|
+
name: zod_1.z.string().min(1),
|
|
56
|
+
version: zod_1.z.string().min(1),
|
|
57
|
+
/** semver constraint on the host binary, e.g. "cv-agent >= 2.0.0". */
|
|
58
|
+
base: zod_1.z.string().min(1),
|
|
59
|
+
/** Path to the system prompt, relative to the manifest (or absolute). */
|
|
60
|
+
system_prompt: zod_1.z.string().min(1),
|
|
61
|
+
tools: ToolsSchema,
|
|
62
|
+
task_types: zod_1.z.array(zod_1.z.string().min(1)).min(1),
|
|
63
|
+
/** The MAXIMUM credential scopes this profile may hold; the ceiling. */
|
|
64
|
+
credential_scopes: zod_1.z.array(zod_1.z.string().min(1)).default([]),
|
|
65
|
+
limits: LimitsSchema.default({}),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
class ProfileError extends Error {
|
|
69
|
+
detail;
|
|
70
|
+
constructor(message, detail = {}) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.detail = detail;
|
|
73
|
+
this.name = 'ProfileError';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.ProfileError = ProfileError;
|
|
77
|
+
/** Validate an already-parsed object as a manifest. Throws ProfileError on any issue. */
|
|
78
|
+
function validateManifest(input) {
|
|
79
|
+
const result = exports.ProfileManifestSchema.safeParse(input);
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
const issues = result.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ');
|
|
82
|
+
throw new ProfileError(`invalid profile manifest: ${issues}`, { issues: result.error.issues });
|
|
83
|
+
}
|
|
84
|
+
return result.data;
|
|
85
|
+
}
|
|
86
|
+
const OPS = ['>=', '<=', '>', '<', '='];
|
|
87
|
+
function parseSemver(v) {
|
|
88
|
+
const m = v.trim().match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
89
|
+
if (!m)
|
|
90
|
+
throw new ProfileError(`not a semver (major.minor.patch): ${v}`);
|
|
91
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
92
|
+
}
|
|
93
|
+
function cmpSemver(a, b) {
|
|
94
|
+
for (let i = 0; i < 3; i++) {
|
|
95
|
+
if (a[i] !== b[i])
|
|
96
|
+
return a[i] < b[i] ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check the manifest `base` constraint against the running host. The grammar is
|
|
102
|
+
* frozen (SPEC): `[<host-name>] <op> <version>`, op one of >= > <= < =, version a
|
|
103
|
+
* bare `major.minor.patch`; a bare version with no op means `=`. A host-name token,
|
|
104
|
+
* when present, must match hostName (a profile for a different host is refused).
|
|
105
|
+
*/
|
|
106
|
+
function checkBaseConstraint(base, hostName, hostVersion) {
|
|
107
|
+
const m = base.trim().match(/^(?:([A-Za-z][A-Za-z0-9_-]*)\s+)?(>=|<=|>|<|=)?\s*(\d+\.\d+\.\d+)$/);
|
|
108
|
+
if (!m)
|
|
109
|
+
return { ok: false, reason: `unparseable base constraint: "${base}" (expected e.g. "cv-agent >= 2.0.0")` };
|
|
110
|
+
const [, name, opRaw, wantVersion] = m;
|
|
111
|
+
if (name && name !== hostName) {
|
|
112
|
+
return { ok: false, reason: `base names host "${name}", but this is "${hostName}"` };
|
|
113
|
+
}
|
|
114
|
+
const op = (opRaw ?? '=');
|
|
115
|
+
if (!OPS.includes(op))
|
|
116
|
+
return { ok: false, reason: `unsupported operator "${op}" in base constraint` };
|
|
117
|
+
const host = parseSemver(hostVersion);
|
|
118
|
+
const want = parseSemver(wantVersion);
|
|
119
|
+
const c = cmpSemver(host, want);
|
|
120
|
+
const satisfied = (op === '>=' && c >= 0) ||
|
|
121
|
+
(op === '>' && c > 0) ||
|
|
122
|
+
(op === '<=' && c <= 0) ||
|
|
123
|
+
(op === '<' && c < 0) ||
|
|
124
|
+
(op === '=' && c === 0);
|
|
125
|
+
if (!satisfied) {
|
|
126
|
+
return { ok: false, reason: `host ${hostName} ${hostVersion} does not satisfy base "${base}"` };
|
|
127
|
+
}
|
|
128
|
+
return { ok: true };
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Tool policy
|
|
132
|
+
// ============================================================================
|
|
133
|
+
/**
|
|
134
|
+
* A tool spec's base name. Claude Code tools appear as "Bash" or scoped like
|
|
135
|
+
* "Bash(git*)"; policy entries may be "Bash", "Bash(*)", or "Bash(git*)". Policy is
|
|
136
|
+
* evaluated by base name so an allow/deny of "Bash" covers every Bash invocation.
|
|
137
|
+
*/
|
|
138
|
+
function toolBaseName(spec) {
|
|
139
|
+
const paren = spec.indexOf('(');
|
|
140
|
+
return (paren === -1 ? spec : spec.slice(0, paren)).trim();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The tool-policy decision: a deny-list entry wins over an allow-list entry, and
|
|
144
|
+
* absence from the allow-list means denied. This is the hard gate the relay and the
|
|
145
|
+
* auto-approve allowlist are both built from.
|
|
146
|
+
*/
|
|
147
|
+
function evaluateTool(manifest, tool) {
|
|
148
|
+
const base = toolBaseName(tool);
|
|
149
|
+
const deny = manifest.tools.deny.map(toolBaseName);
|
|
150
|
+
const allow = manifest.tools.allow.map(toolBaseName);
|
|
151
|
+
if (deny.includes(base))
|
|
152
|
+
return { allowed: false, reason: `tool "${base}" is on the profile deny-list` };
|
|
153
|
+
if (allow.includes(base))
|
|
154
|
+
return { allowed: true };
|
|
155
|
+
return { allowed: false, reason: `tool "${base}" is not on the profile allow-list` };
|
|
156
|
+
}
|
|
157
|
+
/** The `--allowedTools` list for auto-approve mode: allow entries not shadowed by a deny. */
|
|
158
|
+
function allowedToolsList(manifest) {
|
|
159
|
+
const deny = new Set(manifest.tools.deny.map(toolBaseName));
|
|
160
|
+
return manifest.tools.allow.filter((spec) => !deny.has(toolBaseName(spec)));
|
|
161
|
+
}
|
|
162
|
+
/** Pull the tool name out of a Claude Code permission prompt, or null if not present. */
|
|
163
|
+
function extractToolFromPrompt(promptText) {
|
|
164
|
+
const m = promptText.match(/Allow\s+([A-Za-z][A-Za-z0-9_]*)\b/);
|
|
165
|
+
return m ? m[1] : null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The relay decision for a permission prompt under a profile. A tool the profile
|
|
169
|
+
* denies is refused AT THE RELAY (action "deny" -> the relay answers no); an allowed
|
|
170
|
+
* (or unidentifiable) tool falls through to the normal human/CV-Hub relay. The profile
|
|
171
|
+
* only hard-denies; it never auto-approves what the operator would otherwise gate.
|
|
172
|
+
*/
|
|
173
|
+
function relayPermissionDecision(manifest, promptText) {
|
|
174
|
+
const tool = extractToolFromPrompt(promptText);
|
|
175
|
+
if (!tool)
|
|
176
|
+
return { action: 'relay' };
|
|
177
|
+
const decision = evaluateTool(manifest, tool);
|
|
178
|
+
if (!decision.allowed)
|
|
179
|
+
return { action: 'deny', tool, reason: decision.reason };
|
|
180
|
+
return { action: 'relay', tool };
|
|
181
|
+
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Task-type routing + scope ceiling
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/** A dispatched task whose type is outside `task_types` is refused (before any model call). */
|
|
186
|
+
function checkTaskType(manifest, taskType) {
|
|
187
|
+
if (manifest.task_types.includes(taskType))
|
|
188
|
+
return { allowed: true };
|
|
189
|
+
return {
|
|
190
|
+
allowed: false,
|
|
191
|
+
reason: `task type "${taskType}" is not permitted by profile "${manifest.name}" (allowed: ${manifest.task_types.join(', ')})`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Apply the credential-scope ceiling: keep only offered scopes that the profile
|
|
196
|
+
* permits, dropping anything broader. The profile's credential_scopes is a MAXIMUM,
|
|
197
|
+
* so this is the intersection (offered that is within the ceiling).
|
|
198
|
+
*/
|
|
199
|
+
function capScopes(ceiling, offered) {
|
|
200
|
+
const max = new Set(ceiling);
|
|
201
|
+
return offered.filter((s) => max.has(s));
|
|
202
|
+
}
|
|
203
|
+
function defaultRead(path) {
|
|
204
|
+
return (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Resolve a profile spec to a manifest file path. A spec beginning with "@" (or any
|
|
208
|
+
* bare package name) is resolved as a package whose root holds profile.yaml; anything
|
|
209
|
+
* else is a filesystem path (a directory implies <dir>/profile.yaml, a file is used
|
|
210
|
+
* as-is).
|
|
211
|
+
*/
|
|
212
|
+
function resolveProfileSpec(spec, opts) {
|
|
213
|
+
if (spec.startsWith('@') || (!spec.includes('/') && !spec.includes('\\') && !spec.endsWith('.yaml') && !spec.endsWith('.yml'))) {
|
|
214
|
+
const resolver = opts.resolvePackage ?? ((s) => require.resolve(`${s}/profile.yaml`));
|
|
215
|
+
return resolver(spec);
|
|
216
|
+
}
|
|
217
|
+
if (spec.endsWith('.yaml') || spec.endsWith('.yml'))
|
|
218
|
+
return spec;
|
|
219
|
+
return (0, node_path_1.join)(spec, 'profile.yaml');
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Load and validate a profile: resolve the spec, parse + strictly validate the
|
|
223
|
+
* manifest, gate the host version against `base`, then load the system prompt. Every
|
|
224
|
+
* failure is a ProfileError with a specific reason (P14: unknown keys, bad semver, and
|
|
225
|
+
* a failed base gate are all hard errors).
|
|
226
|
+
*/
|
|
227
|
+
function loadProfile(spec, opts) {
|
|
228
|
+
const read = opts.readFile ?? defaultRead;
|
|
229
|
+
const manifestPath = resolveProfileSpec(spec, opts);
|
|
230
|
+
let rawText;
|
|
231
|
+
try {
|
|
232
|
+
rawText = read(manifestPath);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new ProfileError(`could not read profile manifest at ${manifestPath}: ${err.message}`);
|
|
236
|
+
}
|
|
237
|
+
let parsed;
|
|
238
|
+
try {
|
|
239
|
+
parsed = (0, js_yaml_1.load)(rawText);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
throw new ProfileError(`profile manifest is not valid YAML: ${err.message}`);
|
|
243
|
+
}
|
|
244
|
+
if (parsed === null || typeof parsed !== 'object') {
|
|
245
|
+
throw new ProfileError('profile manifest must be a YAML mapping');
|
|
246
|
+
}
|
|
247
|
+
const manifest = validateManifest(parsed);
|
|
248
|
+
const gate = checkBaseConstraint(manifest.base, opts.hostName ?? 'cv-agent', opts.hostVersion);
|
|
249
|
+
if (!gate.ok) {
|
|
250
|
+
throw new ProfileError(`profile "${manifest.name}" base gate failed: ${gate.reason}`, { base: manifest.base, hostVersion: opts.hostVersion });
|
|
251
|
+
}
|
|
252
|
+
const dir = (0, node_path_1.dirname)(manifestPath);
|
|
253
|
+
const promptPath = (0, node_path_1.isAbsolute)(manifest.system_prompt) ? manifest.system_prompt : (0, node_path_1.resolve)(dir, manifest.system_prompt);
|
|
254
|
+
let systemPrompt;
|
|
255
|
+
try {
|
|
256
|
+
systemPrompt = read(promptPath);
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
throw new ProfileError(`could not read system_prompt at ${promptPath}: ${err.message}`);
|
|
260
|
+
}
|
|
261
|
+
if (!systemPrompt.trim()) {
|
|
262
|
+
throw new ProfileError(`system_prompt at ${promptPath} is empty`);
|
|
263
|
+
}
|
|
264
|
+
return { manifest, systemPrompt, dir, source: spec };
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/utils/profile.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@controlvector/cv-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Standalone agent daemon for CV-Hub — bridges Claude Code with CV-Hub task dispatch",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cva": "./dist/bundle.cjs"
|
|
@@ -12,16 +12,23 @@
|
|
|
12
12
|
"test": "vitest run",
|
|
13
13
|
"test:watch": "vitest",
|
|
14
14
|
"clean": "rm -rf dist",
|
|
15
|
-
"
|
|
15
|
+
"verify-pack": "node scripts/verify-pack.mjs",
|
|
16
|
+
"prepublishOnly": "npm run clean && npm run build && npm run verify-pack"
|
|
16
17
|
},
|
|
17
|
-
"keywords": [
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cv-hub",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"agent",
|
|
22
|
+
"controlvector"
|
|
23
|
+
],
|
|
18
24
|
"author": "Control Vector LLC",
|
|
19
|
-
"license": "
|
|
25
|
+
"license": "Apache-2.0",
|
|
20
26
|
"repository": {
|
|
21
27
|
"type": "git",
|
|
22
|
-
"url": "https://hub.controlvector.io/
|
|
28
|
+
"url": "https://git.hub.controlvector.io/controlvector/cv-agent.git"
|
|
23
29
|
},
|
|
24
30
|
"devDependencies": {
|
|
31
|
+
"@types/js-yaml": "^4.0.9",
|
|
25
32
|
"@types/node": "^22.0.0",
|
|
26
33
|
"chalk": "^5.3.0",
|
|
27
34
|
"commander": "^12.1.0",
|
|
@@ -30,12 +37,15 @@
|
|
|
30
37
|
"vitest": "^2.1.0"
|
|
31
38
|
},
|
|
32
39
|
"dependencies": {
|
|
33
|
-
"@controlvector/cv-git": "^1.4.0"
|
|
40
|
+
"@controlvector/cv-git": "^1.4.0",
|
|
41
|
+
"js-yaml": "^5.2.1",
|
|
42
|
+
"zod": "^4.4.3"
|
|
34
43
|
},
|
|
35
44
|
"engines": {
|
|
36
45
|
"node": ">=20"
|
|
37
46
|
},
|
|
38
47
|
"files": [
|
|
39
|
-
"dist"
|
|
48
|
+
"dist",
|
|
49
|
+
"NOTICE"
|
|
40
50
|
]
|
|
41
51
|
}
|