@guilz-dev/belay 0.4.0 → 0.6.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/README.md +7 -1
- package/dist/adapters/layouts/scope.js +3 -2
- package/dist/adapters/shared/gate-runtime.d.ts +1 -2
- package/dist/adapters/shared/gate-runtime.js +226 -28
- package/dist/bundle/claude-runtime.mjs +6967 -4078
- package/dist/bundle/codex-runtime.mjs +5951 -3064
- package/dist/bundle/cursor-runtime.mjs +5951 -3064
- package/dist/cli.js +43 -1
- package/dist/commands/classify-for-report.js +1 -1
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +134 -11
- package/dist/commands/doctor.js +36 -1
- package/dist/commands/explain.js +11 -1
- package/dist/commands/judge.js +3 -2
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.js +51 -0
- package/dist/config-io.js +49 -12
- package/dist/conformance/guarantee-posture.d.ts +21 -0
- package/dist/conformance/guarantee-posture.js +46 -0
- package/dist/conformance/guarantee-table.d.ts +1 -0
- package/dist/conformance/guarantee-table.js +14 -9
- package/dist/core/approval-service.js +6 -2
- package/dist/core/approval.d.ts +2 -0
- package/dist/core/approval.js +14 -3
- package/dist/core/capability/approval-state-mutation.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.js +92 -0
- package/dist/core/capability/approval-v3.d.ts +15 -0
- package/dist/core/capability/approval-v3.js +74 -0
- package/dist/core/capability/attestation.d.ts +13 -0
- package/dist/core/capability/attestation.js +55 -0
- package/dist/core/capability/boundary-attestation-sign.d.ts +21 -0
- package/dist/core/capability/boundary-attestation-sign.js +84 -0
- package/dist/core/capability/boundary-driver-container.d.ts +7 -0
- package/dist/core/capability/boundary-driver-container.js +122 -0
- package/dist/core/capability/boundary-driver.d.ts +29 -0
- package/dist/core/capability/boundary-driver.js +43 -0
- package/dist/core/capability/boundary-egress.d.ts +21 -0
- package/dist/core/capability/boundary-egress.js +109 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +13 -0
- package/dist/core/capability/boundary-grant-materialize.js +78 -0
- package/dist/core/capability/boundary-profile.d.ts +15 -0
- package/dist/core/capability/boundary-profile.js +25 -0
- package/dist/core/capability/boundary-run.d.ts +23 -0
- package/dist/core/capability/boundary-run.js +23 -0
- package/dist/core/capability/boundary-session.d.ts +57 -0
- package/dist/core/capability/boundary-session.js +129 -0
- package/dist/core/capability/capability-request-hash.d.ts +2 -0
- package/dist/core/capability/capability-request-hash.js +8 -0
- package/dist/core/capability/gate-latency-ratchet.d.ts +7 -0
- package/dist/core/capability/gate-latency-ratchet.js +18 -0
- package/dist/core/capability/gate-policy-shadow.d.ts +25 -0
- package/dist/core/capability/gate-policy-shadow.js +108 -0
- package/dist/core/capability/gate-shadow-audit.d.ts +1 -0
- package/dist/core/capability/gate-shadow-audit.js +1 -0
- package/dist/core/capability/gate-shadow-ratchet.d.ts +14 -0
- package/dist/core/capability/gate-shadow-ratchet.js +81 -0
- package/dist/core/capability/grant-lease.d.ts +9 -0
- package/dist/core/capability/grant-lease.js +46 -0
- package/dist/core/capability/grant-loader.d.ts +12 -0
- package/dist/core/capability/grant-loader.js +23 -0
- package/dist/core/capability/grant-match.d.ts +6 -0
- package/dist/core/capability/grant-match.js +93 -0
- package/dist/core/capability/grant.d.ts +16 -0
- package/dist/core/capability/grant.js +16 -0
- package/dist/core/capability/index.d.ts +8 -0
- package/dist/core/capability/index.js +7 -0
- package/dist/core/capability/limits.d.ts +7 -0
- package/dist/core/capability/limits.js +55 -0
- package/dist/core/capability/policy-bridge.d.ts +18 -0
- package/dist/core/capability/policy-bridge.js +51 -0
- package/dist/core/capability/policy-engine.d.ts +70 -0
- package/dist/core/capability/policy-engine.js +476 -0
- package/dist/core/capability/policy-types.d.ts +20 -0
- package/dist/core/capability/policy-types.js +1 -0
- package/dist/core/capability/request.d.ts +45 -0
- package/dist/core/capability/request.js +1 -0
- package/dist/core/capability/resolver.d.ts +4 -0
- package/dist/core/capability/resolver.js +4 -0
- package/dist/core/classify-subagent.d.ts +2 -1
- package/dist/core/classify-subagent.js +38 -2
- package/dist/core/classify-tool.js +126 -62
- package/dist/core/config.d.ts +14 -3
- package/dist/core/config.js +56 -3
- package/dist/core/gate-contract.d.ts +3 -0
- package/dist/core/gate-contract.js +3 -0
- package/dist/core/gate-engine.js +6 -1
- package/dist/core/judge-doctor.js +39 -14
- package/dist/core/judge-fallback-hints.d.ts +13 -0
- package/dist/core/judge-fallback-hints.js +108 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +5 -2
- package/dist/core/recovery/capability.d.ts +3 -0
- package/dist/core/recovery/capability.js +22 -0
- package/dist/core/recovery/fail-closed.d.ts +6 -0
- package/dist/core/recovery/fail-closed.js +25 -0
- package/dist/core/recovery/index.d.ts +3 -0
- package/dist/core/recovery/index.js +2 -0
- package/dist/core/recovery/types.d.ts +20 -0
- package/dist/core/recovery/types.js +1 -0
- package/dist/core/standing-allow.js +3 -0
- package/dist/core/transactional/diff-evaluator.js +4 -1
- package/dist/core/transactional/eligibility.js +4 -0
- package/dist/core/transactional/git-worktree.d.ts +10 -3
- package/dist/core/transactional/git-worktree.js +81 -11
- package/dist/core/transactional/reasons.d.ts +1 -0
- package/dist/core/transactional/reasons.js +4 -0
- package/dist/core/transactional/runner.js +22 -6
- package/dist/core/transactional/types.d.ts +4 -0
- package/dist/core/types.d.ts +21 -3
- package/dist/core/verdict/adapter.d.ts +3 -4
- package/dist/core/verdict/adapter.js +16 -12
- package/dist/core/verdict/cursor-acp-client.d.ts +67 -0
- package/dist/core/verdict/cursor-acp-client.js +345 -0
- package/dist/core/verdict/egress-classify.d.ts +2 -2
- package/dist/core/verdict/egress-classify.js +2 -2
- package/dist/core/verdict/git-classifier.d.ts +26 -0
- package/dist/core/verdict/git-classifier.js +533 -0
- package/dist/core/verdict/judge-audit.js +1 -0
- package/dist/core/verdict/judge-baseline.d.ts +1 -1
- package/dist/core/verdict/judge-baseline.js +1 -0
- package/dist/core/verdict/judge-broker-service.js +12 -0
- package/dist/core/verdict/judge-catalog.js +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.d.ts +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.js +6 -0
- package/dist/core/verdict/judge-cli.js +5 -3
- package/dist/core/verdict/judge-provider-matrix.js +1 -1
- package/dist/core/verdict/judge-runtime-config.d.ts +2 -0
- package/dist/core/verdict/judge-runtime-config.js +10 -6
- package/dist/core/verdict/judge-session-broker.d.ts +1 -1
- package/dist/core/verdict/judge-session-broker.js +10 -5
- package/dist/core/verdict/judge-transport.d.ts +5 -1
- package/dist/core/verdict/judge-transport.js +24 -14
- package/dist/core/verdict/judge.d.ts +2 -15
- package/dist/core/verdict/judge.js +5 -68
- package/dist/core/verdict/launcher-resolve.d.ts +2 -0
- package/dist/core/verdict/launcher-resolve.js +16 -0
- package/dist/core/verdict/prescan.d.ts +15 -0
- package/dist/core/verdict/prescan.js +68 -0
- package/dist/core/verdict/shell-policy.d.ts +25 -0
- package/dist/core/verdict/shell-policy.js +40 -0
- package/dist/core/verdict/shell-semantics.d.ts +24 -0
- package/dist/core/verdict/shell-semantics.js +225 -0
- package/dist/core/verdict/types.d.ts +13 -1
- package/dist/core/verdict/verdict.js +296 -178
- package/dist/corpus/evaluate.js +3 -7
- package/dist/corpus/gate-latency-budget.d.ts +39 -0
- package/dist/corpus/gate-latency-budget.js +33 -0
- package/dist/corpus/mutators.d.ts +1 -1
- package/dist/corpus/mutators.js +11 -0
- package/dist/corpus/runtime-match.js +1 -3
- package/dist/corpus/standing-allow-catalog.generated.js +29 -9
- package/dist/judge-broker-daemon.js +15 -1
- package/dist/services/sandbox-service.d.ts +4 -0
- package/dist/services/sandbox-service.js +28 -2
- package/dist/types.d.ts +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { BOUNDARY_PROFILE_L3_L4_ONLY } from './capability/boundary-profile.js';
|
|
2
|
+
import { policyReasonToLegacyReason } from './capability/policy-bridge.js';
|
|
3
|
+
import { evaluateSubagentPolicy, policyDecisionRequiresAsk } from './capability/policy-engine.js';
|
|
1
4
|
import { canonicalStringify, subagentFingerprint } from './fingerprint.js';
|
|
2
5
|
import { subagentFingerprintSource } from './replay-scrub.js';
|
|
3
6
|
import { scrubValue } from './scrub.js';
|
|
@@ -25,7 +28,7 @@ function extractSubagentText(payload, options) {
|
|
|
25
28
|
function fingerprintSource(payload, options) {
|
|
26
29
|
return subagentFingerprintSource(payload, options.scrubOptions ?? {});
|
|
27
30
|
}
|
|
28
|
-
export function classifySubagent(payload, repoRoot, options = {}) {
|
|
31
|
+
export function classifySubagent(payload, repoRoot, options = {}, config) {
|
|
29
32
|
const kind = payload.tool_name === 'Task' ? 'Task' : String(payload.subagent_type ?? 'generalPurpose');
|
|
30
33
|
const scrubbed = fingerprintSource(payload, options);
|
|
31
34
|
const summary = extractSubagentText(payload, options);
|
|
@@ -35,6 +38,36 @@ export function classifySubagent(payload, repoRoot, options = {}) {
|
|
|
35
38
|
const pattern = new RegExp(`\\b${term}\\b`, 'i');
|
|
36
39
|
return pattern.test(lowered);
|
|
37
40
|
});
|
|
41
|
+
const signals = hasExternalTerm ? ['subagent_external_intent_hint'] : ['subagent_default_review'];
|
|
42
|
+
const { request, decision } = evaluateSubagentPolicy({
|
|
43
|
+
subagentType: kind,
|
|
44
|
+
summary,
|
|
45
|
+
repoRoot,
|
|
46
|
+
cwd: repoRoot,
|
|
47
|
+
inputFingerprint: fingerprint,
|
|
48
|
+
signals,
|
|
49
|
+
}, config, {
|
|
50
|
+
grants: options.grants,
|
|
51
|
+
attestation: options.attestation,
|
|
52
|
+
egressProxyActive: options.egressProxyActive,
|
|
53
|
+
});
|
|
54
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
55
|
+
return {
|
|
56
|
+
verdict: 'deny_pending_approval',
|
|
57
|
+
reason: policyReasonToLegacyReason(decision),
|
|
58
|
+
summary,
|
|
59
|
+
fingerprint,
|
|
60
|
+
assessment: {
|
|
61
|
+
reversibility: 'recoverable_with_cost',
|
|
62
|
+
external: hasExternalTerm,
|
|
63
|
+
blastRadius: 'subagent task scope',
|
|
64
|
+
confidence: hasExternalTerm ? 0.7 : 0.67,
|
|
65
|
+
signals: [...signals, ...decision.signals],
|
|
66
|
+
},
|
|
67
|
+
capabilityRequests: [request],
|
|
68
|
+
authorizationDecision: decision,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
38
71
|
return {
|
|
39
72
|
verdict: 'allow_flagged',
|
|
40
73
|
reason: 'subagent_review',
|
|
@@ -45,7 +78,10 @@ export function classifySubagent(payload, repoRoot, options = {}) {
|
|
|
45
78
|
external: false,
|
|
46
79
|
blastRadius: 'subagent task scope',
|
|
47
80
|
confidence: hasExternalTerm ? 0.7 : 0.67,
|
|
48
|
-
signals
|
|
81
|
+
signals,
|
|
49
82
|
},
|
|
83
|
+
capabilityRequests: [request],
|
|
84
|
+
authorizationDecision: decision,
|
|
85
|
+
boundaryProfile: options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
50
86
|
};
|
|
51
87
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { BOUNDARY_PROFILE_L3_L4_ONLY } from './capability/boundary-profile.js';
|
|
3
|
+
import { policyReasonToLegacyReason } from './capability/policy-bridge.js';
|
|
4
|
+
import { evaluateFileMutationPolicy, policyDecisionRequiresAsk, } from './capability/policy-engine.js';
|
|
2
5
|
import { canonicalStringify, toolFingerprint } from './fingerprint.js';
|
|
3
6
|
import { matchesSensitivePath } from './glob.js';
|
|
4
7
|
import { pathWithinRoot, resolveWorkspaceRootMatch } from './path-utils.js';
|
|
5
8
|
import { scrubValue } from './scrub.js';
|
|
6
9
|
import { classifyShell, resolveClassifierTrustedCwd } from './verdict/adapter.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
10
|
+
import { isGitPath } from './verdict/containment.js';
|
|
11
|
+
import { mutationPrescanRequiresAsk } from './verdict/prescan.js';
|
|
9
12
|
const DEFAULT_SENSITIVE_PATHS = ['.env', '.env.*', '**/credentials/**'];
|
|
10
13
|
const FILE_WRITE_TOOL_NAMES = new Set(['write']);
|
|
11
14
|
const FILE_EDIT_TOOL_NAMES = new Set([
|
|
@@ -18,6 +21,19 @@ const FILE_EDIT_TOOL_NAMES = new Set([
|
|
|
18
21
|
]);
|
|
19
22
|
const FILE_DELETE_TOOL_NAMES = new Set(['delete']);
|
|
20
23
|
const APPLY_PATCH_TOOL_NAMES = new Set(['apply_patch', 'applypatch']);
|
|
24
|
+
function policyAuth(options) {
|
|
25
|
+
if (!options.grants &&
|
|
26
|
+
options.attestation === undefined &&
|
|
27
|
+
options.egressProxyActive === undefined) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
grants: options.grants,
|
|
32
|
+
attestation: options.attestation,
|
|
33
|
+
egressProxyActive: options.egressProxyActive,
|
|
34
|
+
sensitivePaths: options.sensitivePaths,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
21
37
|
function scrubPayload(value, options) {
|
|
22
38
|
return scrubValue(value, options.scrubOptions);
|
|
23
39
|
}
|
|
@@ -76,11 +92,7 @@ function applyPatchTargets(patch) {
|
|
|
76
92
|
function normalizedToolName(toolName) {
|
|
77
93
|
return toolName.trim().toLowerCase();
|
|
78
94
|
}
|
|
79
|
-
function
|
|
80
|
-
return options.tier1Judge ?? createJudgeFromConfig(config, { repoRoot });
|
|
81
|
-
}
|
|
82
|
-
async function classifyFileMutationWithTier1(params) {
|
|
83
|
-
const judge = resolveFileTier1Judge(params.config, params.options, params.repoRoot);
|
|
95
|
+
function classifyFileMutationWithPolicy(params) {
|
|
84
96
|
const trustedCwd = resolveClassifierTrustedCwd(params.cwd, params.options);
|
|
85
97
|
const prescan = mutationPrescanRequiresAsk({
|
|
86
98
|
targets: [params.filePath],
|
|
@@ -91,11 +103,29 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
91
103
|
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
92
104
|
});
|
|
93
105
|
if (prescan) {
|
|
106
|
+
const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
|
|
107
|
+
const { request, decision } = evaluateFileMutationPolicy({
|
|
108
|
+
hookKind: 'tool',
|
|
109
|
+
toolKind: params.toolKind,
|
|
110
|
+
filePath: params.filePath,
|
|
111
|
+
resolvedPath: params.resolvedPath,
|
|
112
|
+
repoRoot: params.repoRoot,
|
|
113
|
+
cwd: params.cwd,
|
|
114
|
+
inputFingerprint: fingerprint,
|
|
115
|
+
signals: [...params.signals, 'tier1_catastrophic', prescan.reason],
|
|
116
|
+
isDelete: params.isDelete,
|
|
117
|
+
locationLabel: params.locationLabel,
|
|
118
|
+
trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
|
|
119
|
+
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
120
|
+
}, params.config, policyAuth(params.options));
|
|
121
|
+
const legacyReason = policyDecisionRequiresAsk(decision)
|
|
122
|
+
? policyReasonToLegacyReason(decision)
|
|
123
|
+
: 'tier1_catastrophic';
|
|
94
124
|
return {
|
|
95
125
|
verdict: 'deny_pending_approval',
|
|
96
|
-
reason:
|
|
126
|
+
reason: legacyReason,
|
|
97
127
|
summary: params.filePath,
|
|
98
|
-
fingerprint
|
|
128
|
+
fingerprint,
|
|
99
129
|
assessment: {
|
|
100
130
|
reversibility: 'irreversible',
|
|
101
131
|
external: params.locationLabel === 'outside_repo',
|
|
@@ -103,20 +133,32 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
103
133
|
? 'outside the repository'
|
|
104
134
|
: 'sensitive repository file',
|
|
105
135
|
confidence: 0.95,
|
|
106
|
-
signals: [...params.signals, 'tier1_catastrophic', prescan.reason],
|
|
136
|
+
signals: [...params.signals, 'tier1_catastrophic', prescan.reason, ...decision.signals],
|
|
107
137
|
},
|
|
138
|
+
capabilityRequests: [request],
|
|
139
|
+
authorizationDecision: decision,
|
|
140
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
108
141
|
};
|
|
109
142
|
}
|
|
110
|
-
const tier1Text = `file_mutation: ${params.toolKind} path=${params.resolvedPath} location=${params.locationLabel}`;
|
|
111
|
-
const tier1 = await judge.evaluate({
|
|
112
|
-
text: tier1Text,
|
|
113
|
-
context: { cwd: params.cwd, repoRoot: params.repoRoot },
|
|
114
|
-
});
|
|
115
143
|
const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
|
|
116
|
-
|
|
144
|
+
const { request, decision } = evaluateFileMutationPolicy({
|
|
145
|
+
hookKind: 'tool',
|
|
146
|
+
toolKind: params.toolKind,
|
|
147
|
+
filePath: params.filePath,
|
|
148
|
+
resolvedPath: params.resolvedPath,
|
|
149
|
+
repoRoot: params.repoRoot,
|
|
150
|
+
cwd: params.cwd,
|
|
151
|
+
inputFingerprint: fingerprint,
|
|
152
|
+
signals: params.signals,
|
|
153
|
+
isDelete: params.isDelete,
|
|
154
|
+
locationLabel: params.locationLabel,
|
|
155
|
+
trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
|
|
156
|
+
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
157
|
+
}, params.config, policyAuth(params.options));
|
|
158
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
117
159
|
return {
|
|
118
160
|
verdict: 'deny_pending_approval',
|
|
119
|
-
reason:
|
|
161
|
+
reason: policyReasonToLegacyReason(decision),
|
|
120
162
|
summary: params.filePath,
|
|
121
163
|
fingerprint,
|
|
122
164
|
assessment: {
|
|
@@ -125,9 +167,12 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
125
167
|
blastRadius: params.locationLabel === 'outside_repo'
|
|
126
168
|
? 'outside the repository'
|
|
127
169
|
: 'sensitive repository file',
|
|
128
|
-
confidence: 0.
|
|
129
|
-
signals: [...params.signals,
|
|
170
|
+
confidence: 0.95,
|
|
171
|
+
signals: [...params.signals, ...decision.signals],
|
|
130
172
|
},
|
|
173
|
+
capabilityRequests: [request],
|
|
174
|
+
authorizationDecision: decision,
|
|
175
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
131
176
|
};
|
|
132
177
|
}
|
|
133
178
|
const reason = params.isDelete ? 'file_delete' : 'file_mutation';
|
|
@@ -141,8 +186,11 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
141
186
|
external: params.locationLabel === 'outside_repo',
|
|
142
187
|
blastRadius: params.locationLabel === 'outside_repo' ? 'outside the repository' : 'this repository',
|
|
143
188
|
confidence: 0.72,
|
|
144
|
-
signals: [...params.signals,
|
|
189
|
+
signals: [...params.signals, ...decision.signals],
|
|
145
190
|
},
|
|
191
|
+
capabilityRequests: [request],
|
|
192
|
+
authorizationDecision: decision,
|
|
193
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
146
194
|
};
|
|
147
195
|
}
|
|
148
196
|
export async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
@@ -230,24 +278,24 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
230
278
|
const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
|
|
231
279
|
if (hitsProtectedRoot) {
|
|
232
280
|
signals.push('control_plane_path');
|
|
233
|
-
return {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
};
|
|
281
|
+
return classifyFileMutationWithPolicy({
|
|
282
|
+
toolName,
|
|
283
|
+
toolKind,
|
|
284
|
+
filePath,
|
|
285
|
+
resolvedPath,
|
|
286
|
+
repoRoot,
|
|
287
|
+
cwd,
|
|
288
|
+
config,
|
|
289
|
+
options,
|
|
290
|
+
signals,
|
|
291
|
+
isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
|
|
292
|
+
locationLabel: 'control_plane',
|
|
293
|
+
});
|
|
246
294
|
}
|
|
247
295
|
const workspaceMatch = resolveWorkspaceRootMatch(repoRoot, options.trustedWorkspaceRoots, resolvedPath);
|
|
248
296
|
if (workspaceMatch === null) {
|
|
249
297
|
signals.push('outside_repo_path');
|
|
250
|
-
return
|
|
298
|
+
return classifyFileMutationWithPolicy({
|
|
251
299
|
toolName,
|
|
252
300
|
toolKind,
|
|
253
301
|
filePath,
|
|
@@ -274,7 +322,7 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
274
322
|
sensitivePaths,
|
|
275
323
|
});
|
|
276
324
|
if (workspacePrescan) {
|
|
277
|
-
return
|
|
325
|
+
return classifyFileMutationWithPolicy({
|
|
278
326
|
toolName,
|
|
279
327
|
toolKind,
|
|
280
328
|
filePath,
|
|
@@ -291,7 +339,7 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
291
339
|
const relativePath = workspaceMatch.relativePath;
|
|
292
340
|
if (matchesSensitivePath(relativePath, sensitivePaths)) {
|
|
293
341
|
signals.push('sensitive_path');
|
|
294
|
-
return
|
|
342
|
+
return classifyFileMutationWithPolicy({
|
|
295
343
|
toolName,
|
|
296
344
|
toolKind,
|
|
297
345
|
filePath,
|
|
@@ -306,35 +354,51 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
306
354
|
});
|
|
307
355
|
}
|
|
308
356
|
if (FILE_DELETE_TOOL_NAMES.has(toolKind)) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
357
|
+
if (isGitPath(resolvedPath, repoRoot)) {
|
|
358
|
+
signals.push('protected_artifact');
|
|
359
|
+
return classifyFileMutationWithPolicy({
|
|
360
|
+
toolName,
|
|
361
|
+
toolKind,
|
|
362
|
+
filePath,
|
|
363
|
+
resolvedPath,
|
|
364
|
+
repoRoot,
|
|
365
|
+
cwd,
|
|
366
|
+
config,
|
|
367
|
+
options,
|
|
320
368
|
signals,
|
|
321
|
-
|
|
322
|
-
|
|
369
|
+
isDelete: true,
|
|
370
|
+
locationLabel: 'sensitive_path',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
signals.push('file_delete');
|
|
374
|
+
return classifyFileMutationWithPolicy({
|
|
375
|
+
toolName,
|
|
376
|
+
toolKind,
|
|
377
|
+
filePath,
|
|
378
|
+
resolvedPath,
|
|
379
|
+
repoRoot,
|
|
380
|
+
cwd,
|
|
381
|
+
config,
|
|
382
|
+
options,
|
|
383
|
+
signals,
|
|
384
|
+
isDelete: true,
|
|
385
|
+
locationLabel: 'repo_local',
|
|
386
|
+
});
|
|
323
387
|
}
|
|
324
388
|
signals.push('file_mutation');
|
|
325
|
-
return {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
};
|
|
389
|
+
return classifyFileMutationWithPolicy({
|
|
390
|
+
toolName,
|
|
391
|
+
toolKind,
|
|
392
|
+
filePath,
|
|
393
|
+
resolvedPath,
|
|
394
|
+
repoRoot,
|
|
395
|
+
cwd,
|
|
396
|
+
config,
|
|
397
|
+
options,
|
|
398
|
+
signals,
|
|
399
|
+
isDelete: false,
|
|
400
|
+
locationLabel: 'repo_local',
|
|
401
|
+
});
|
|
338
402
|
}
|
|
339
403
|
if (APPLY_PATCH_TOOL_NAMES.has(toolKind)) {
|
|
340
404
|
const patch = extractPatch(payload);
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BoundaryDriverId } from './capability/attestation.js';
|
|
2
|
+
import type { BelayMode, ClassifierOptions, ControlPlaneIntegrity, ScrubOptions, UnknownLocalEffectPolicy, UnparseableShellPolicy } from './types.js';
|
|
2
3
|
import { type BelayJudgeRuntimeConfig } from './verdict/judge-runtime-config.js';
|
|
3
|
-
export type BelayMode = 'enforce' | 'audit';
|
|
4
4
|
export type { UnknownLocalEffectPolicy };
|
|
5
5
|
export interface BelayConfigV1 {
|
|
6
6
|
version: 1;
|
|
@@ -94,7 +94,10 @@ export interface JudgeCloudConsent {
|
|
|
94
94
|
endpoint: string;
|
|
95
95
|
by: string;
|
|
96
96
|
}
|
|
97
|
+
export type BelayJudgeMode = 'shadow' | 'off';
|
|
97
98
|
export interface BelayJudgeConfig {
|
|
99
|
+
/** Gate uses PolicyEngine; judge runs async shadow/compare only. */
|
|
100
|
+
mode?: BelayJudgeMode;
|
|
98
101
|
provider: JudgeProvider;
|
|
99
102
|
providerId?: JudgeProviderId | DeprecatedJudgeProviderId;
|
|
100
103
|
model: string;
|
|
@@ -157,8 +160,14 @@ export interface BelayEgressConfig {
|
|
|
157
160
|
/** When true with egress enabled, L3 external command lists become hints only. */
|
|
158
161
|
demoteL3External: boolean;
|
|
159
162
|
}
|
|
163
|
+
export interface BelayCapabilityConfig {
|
|
164
|
+
grantsEnabled?: boolean;
|
|
165
|
+
boundaryDriver?: BoundaryDriverId;
|
|
166
|
+
attestationRelPath?: string;
|
|
167
|
+
}
|
|
168
|
+
export declare const DEFAULT_CAPABILITY_V5: BelayCapabilityConfig;
|
|
160
169
|
export interface BelayConfigV4 {
|
|
161
|
-
version: 4;
|
|
170
|
+
version: 4 | 5;
|
|
162
171
|
adapter?: 'cursor' | 'claude' | 'codex';
|
|
163
172
|
/** Where hooks/runtime/skill artifacts are installed. Defaults to project. */
|
|
164
173
|
installScope?: 'project' | 'global';
|
|
@@ -178,6 +187,7 @@ export interface BelayConfigV4 {
|
|
|
178
187
|
sandbox: BelaySandboxConfig;
|
|
179
188
|
audit: BelayConfigV2['audit'];
|
|
180
189
|
judge: BelayJudgeConfig;
|
|
190
|
+
capability?: BelayCapabilityConfig;
|
|
181
191
|
}
|
|
182
192
|
/** @deprecated Use BelayConfigV4 */
|
|
183
193
|
export type BelayConfigV3 = BelayConfigV4;
|
|
@@ -239,6 +249,7 @@ type RawConfigInput = Partial<{
|
|
|
239
249
|
sandbox: Partial<BelaySandboxConfig>;
|
|
240
250
|
audit: Partial<BelayConfigV2['audit']>;
|
|
241
251
|
installScope: 'project' | 'global';
|
|
252
|
+
capability: Partial<BelayCapabilityConfig>;
|
|
242
253
|
}>;
|
|
243
254
|
export declare function migrateConfig(loaded: unknown): BelayConfigV4;
|
|
244
255
|
export declare function normalizeConfigV2(config: BelayConfigV2): BelayConfigV2;
|
package/dist/core/config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { warnDeprecatedJudgeModelAuto } from './judge-model-policy.js';
|
|
|
3
3
|
import { getJudgeProviderSpec, inferProviderIdFromConfig, isRemovedProviderId, normalizeLegacyProviderId, warnRemovedProviderId, } from './verdict/judge-catalog.js';
|
|
4
4
|
import { normalizeJudgeRuntimeConfig, } from './verdict/judge-runtime-config.js';
|
|
5
5
|
export const DEFAULT_JUDGE_LOCAL_OLLAMA = {
|
|
6
|
+
mode: 'shadow',
|
|
6
7
|
provider: 'ollama',
|
|
7
8
|
providerId: 'ollama',
|
|
8
9
|
model: 'gemma4:e2b',
|
|
@@ -11,6 +12,7 @@ export const DEFAULT_JUDGE_LOCAL_OLLAMA = {
|
|
|
11
12
|
keepAlive: '30m',
|
|
12
13
|
};
|
|
13
14
|
export const DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE = {
|
|
15
|
+
mode: 'shadow',
|
|
14
16
|
provider: 'openai-compatible',
|
|
15
17
|
providerId: 'codex',
|
|
16
18
|
model: 'gpt-5.3-codex-high',
|
|
@@ -20,6 +22,23 @@ export const DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE = {
|
|
|
20
22
|
};
|
|
21
23
|
/** @deprecated Use DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE */
|
|
22
24
|
export const DEFAULT_JUDGE_CURSOR_COMPOSER = DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE;
|
|
25
|
+
export const DEFAULT_CAPABILITY_V5 = {
|
|
26
|
+
grantsEnabled: true,
|
|
27
|
+
boundaryDriver: 'host-integration',
|
|
28
|
+
attestationRelPath: '.belay/attestation.json',
|
|
29
|
+
};
|
|
30
|
+
function normalizeCapabilityConfig(capability) {
|
|
31
|
+
if (!capability) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
...DEFAULT_CAPABILITY_V5,
|
|
36
|
+
...capability,
|
|
37
|
+
grantsEnabled: capability.grantsEnabled !== false,
|
|
38
|
+
boundaryDriver: capability.boundaryDriver ?? DEFAULT_CAPABILITY_V5.boundaryDriver,
|
|
39
|
+
attestationRelPath: capability.attestationRelPath ?? DEFAULT_CAPABILITY_V5.attestationRelPath,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
23
42
|
/** Pre-v0.4 defaults preserved when migrating existing v1/v2/v3 configs. */
|
|
24
43
|
import { DEFAULT_SILENT_PASS_THRESHOLD } from './audit-summary.js';
|
|
25
44
|
/** @deprecated Use DEFAULT_SILENT_PASS_THRESHOLD from audit-summary.js */
|
|
@@ -222,7 +241,8 @@ export function isConfigV3(value) {
|
|
|
222
241
|
return version === 3 || version === 4;
|
|
223
242
|
}
|
|
224
243
|
export function isConfigV4(value) {
|
|
225
|
-
|
|
244
|
+
const version = typeof value === 'object' && value !== null ? value.version : null;
|
|
245
|
+
return version === 4 || version === 5;
|
|
226
246
|
}
|
|
227
247
|
export function normalizeJudgeProvider(provider) {
|
|
228
248
|
if (provider === 'anthropic') {
|
|
@@ -239,6 +259,7 @@ function defaultJudgeTemplateForProvider(provider) {
|
|
|
239
259
|
}
|
|
240
260
|
if (provider === 'anthropic') {
|
|
241
261
|
return {
|
|
262
|
+
mode: 'shadow',
|
|
242
263
|
provider: 'anthropic',
|
|
243
264
|
providerId: 'claude',
|
|
244
265
|
model: 'claude-sonnet-4-6',
|
|
@@ -283,6 +304,7 @@ export function normalizeJudgeConfig(judge) {
|
|
|
283
304
|
const timeoutMs = typeof judge.timeoutMs === 'number' && judge.timeoutMs > 0 ? judge.timeoutMs : base.timeoutMs;
|
|
284
305
|
const endpoint = typeof judge.endpoint === 'string' && judge.endpoint.trim() ? judge.endpoint.trim() : null;
|
|
285
306
|
return {
|
|
307
|
+
mode: judge.mode === 'off' ? 'off' : 'shadow',
|
|
286
308
|
provider,
|
|
287
309
|
providerId: rawProviderId,
|
|
288
310
|
model,
|
|
@@ -320,6 +342,7 @@ export function normalizeJudgeConfig(judge) {
|
|
|
320
342
|
endpoint = catalogSpec.defaultEndpoint;
|
|
321
343
|
}
|
|
322
344
|
const normalized = {
|
|
345
|
+
mode: judge.mode === 'off' ? 'off' : 'shadow',
|
|
323
346
|
provider,
|
|
324
347
|
providerId,
|
|
325
348
|
model,
|
|
@@ -490,6 +513,19 @@ function normalizeV3Raw(raw) {
|
|
|
490
513
|
},
|
|
491
514
|
});
|
|
492
515
|
}
|
|
516
|
+
function normalizeV5Raw(raw) {
|
|
517
|
+
const base = normalizeV3Raw({ ...raw, version: 4 });
|
|
518
|
+
return {
|
|
519
|
+
...base,
|
|
520
|
+
version: 5,
|
|
521
|
+
capability: {
|
|
522
|
+
grantsEnabled: raw.capability?.grantsEnabled !== false,
|
|
523
|
+
boundaryDriver: raw.capability?.boundaryDriver ?? 'host-integration',
|
|
524
|
+
attestationRelPath: raw.capability?.attestationRelPath ?? '.belay/attestation.json',
|
|
525
|
+
...(raw.capability ?? {}),
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
}
|
|
493
529
|
export function migrateConfig(loaded) {
|
|
494
530
|
if (typeof loaded !== 'object' || loaded === null) {
|
|
495
531
|
return { ...DEFAULT_CONFIG_V4 };
|
|
@@ -498,6 +534,9 @@ export function migrateConfig(loaded) {
|
|
|
498
534
|
if (raw.version === 4) {
|
|
499
535
|
return normalizeV3Raw(raw);
|
|
500
536
|
}
|
|
537
|
+
if (raw.version === 5) {
|
|
538
|
+
return normalizeV5Raw(raw);
|
|
539
|
+
}
|
|
501
540
|
if (raw.version === 3 || (raw.version === undefined && hasV3Sections(raw))) {
|
|
502
541
|
return normalizeV3Raw(raw);
|
|
503
542
|
}
|
|
@@ -576,8 +615,9 @@ export function normalizeConfig(config) {
|
|
|
576
615
|
return normalizeConfigV2(config);
|
|
577
616
|
}
|
|
578
617
|
const v4 = config;
|
|
579
|
-
|
|
580
|
-
|
|
618
|
+
const version = v4.version === 5 ? 5 : 4;
|
|
619
|
+
const normalized = {
|
|
620
|
+
version,
|
|
581
621
|
...(v4.installScope === 'global' || v4.installScope === 'project'
|
|
582
622
|
? { installScope: v4.installScope }
|
|
583
623
|
: {}),
|
|
@@ -743,6 +783,10 @@ export function normalizeConfig(config) {
|
|
|
743
783
|
},
|
|
744
784
|
judge: normalizeJudgeConfig(v4.judge ?? DEFAULT_JUDGE_LOCAL_OLLAMA),
|
|
745
785
|
};
|
|
786
|
+
if (version === 5 || v4.capability) {
|
|
787
|
+
normalized.capability = normalizeCapabilityConfig(v4.capability ?? DEFAULT_CAPABILITY_V5);
|
|
788
|
+
}
|
|
789
|
+
return normalized;
|
|
746
790
|
}
|
|
747
791
|
export function isFreshConfigInput(loaded) {
|
|
748
792
|
if (loaded === null || loaded === undefined) {
|
|
@@ -814,6 +858,15 @@ export function mergeConfig(existing, defaults = DEFAULT_CONFIG_V4) {
|
|
|
814
858
|
...migrated.audit,
|
|
815
859
|
},
|
|
816
860
|
...(migrated.installScope ? { installScope: migrated.installScope } : {}),
|
|
861
|
+
...(migrated.version === 5 || migrated.capability
|
|
862
|
+
? {
|
|
863
|
+
version: migrated.version === 5 ? 5 : 4,
|
|
864
|
+
capability: normalizeCapabilityConfig({
|
|
865
|
+
...DEFAULT_CAPABILITY_V5,
|
|
866
|
+
...migrated.capability,
|
|
867
|
+
}),
|
|
868
|
+
}
|
|
869
|
+
: {}),
|
|
817
870
|
});
|
|
818
871
|
}
|
|
819
872
|
export function scrubOptionsFromConfig(config) {
|
|
@@ -29,6 +29,9 @@ export interface GateVerdict extends GatePermissionResponse {
|
|
|
29
29
|
wouldBlock: boolean;
|
|
30
30
|
mode: 'enforce' | 'audit';
|
|
31
31
|
axes?: ClassifyResult['axes'];
|
|
32
|
+
capabilityRequests?: ClassifyResult['capabilityRequests'];
|
|
33
|
+
authorizationDecision?: ClassifyResult['authorizationDecision'];
|
|
34
|
+
boundaryProfile?: string;
|
|
32
35
|
}
|
|
33
36
|
export declare function isGatedAction(value: unknown): value is GatedAction;
|
|
34
37
|
export declare function classifyResultToGateVerdict(params: {
|
|
@@ -26,6 +26,9 @@ export function classifyResultToGateVerdict(params) {
|
|
|
26
26
|
user_message,
|
|
27
27
|
agent_message,
|
|
28
28
|
axes: result.axes,
|
|
29
|
+
capabilityRequests: result.capabilityRequests,
|
|
30
|
+
authorizationDecision: result.authorizationDecision,
|
|
31
|
+
boundaryProfile: result.boundaryProfile,
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
export function unnormalizedGateVerdict(params) {
|
package/dist/core/gate-engine.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { allPathsAllowlisted } from './capability/allowlist.js';
|
|
2
|
+
import { checkGatedActionLimits } from './capability/limits.js';
|
|
2
3
|
import { collectOutsideRepoPaths, collectOutsideRepoPathsFromToolPayload, } from './capability/paths.js';
|
|
3
4
|
import { classifySubagent } from './classify-subagent.js';
|
|
4
5
|
import { classifyToolUse } from './classify-tool.js';
|
|
@@ -197,6 +198,10 @@ function applyToolSandboxPolicies(action, result, options) {
|
|
|
197
198
|
return next;
|
|
198
199
|
}
|
|
199
200
|
export async function classifyGatedAction(action, config, extraOptions = {}) {
|
|
201
|
+
const limitResult = checkGatedActionLimits(action);
|
|
202
|
+
if (limitResult) {
|
|
203
|
+
return limitResult;
|
|
204
|
+
}
|
|
200
205
|
const options = { ...classifierOptionsFromConfig(config), ...extraOptions };
|
|
201
206
|
if (action.kind === 'shell') {
|
|
202
207
|
const command = action.command ?? shellCommandFromPayload(action.payload ?? {});
|
|
@@ -221,7 +226,7 @@ export async function classifyGatedAction(action, config, extraOptions = {}) {
|
|
|
221
226
|
};
|
|
222
227
|
}
|
|
223
228
|
if (action.kind === 'subagent') {
|
|
224
|
-
return classifySubagent(action.payload ?? {}, action.repoRoot, options);
|
|
229
|
+
return classifySubagent(action.payload ?? {}, action.repoRoot, options, config);
|
|
225
230
|
}
|
|
226
231
|
let result = await classifyToolUse(action.payload ?? {}, action.repoRoot, action.cwd, config, options);
|
|
227
232
|
result = applyToolSandboxPolicies(action, result, options);
|