@guilz-dev/belay 0.6.0 → 0.7.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 +20 -1
- package/dist/adapters/claude/runtime-entry.js +54 -25
- package/dist/adapters/shared/gate-runtime.d.ts +7 -0
- package/dist/adapters/shared/gate-runtime.js +420 -220
- package/dist/bundle/claude-runtime.mjs +13215 -8864
- package/dist/bundle/codex-runtime.mjs +10771 -6453
- package/dist/bundle/cursor-runtime.mjs +10797 -6484
- package/dist/cli.js +37 -1
- package/dist/commands/approve.js +34 -10
- package/dist/commands/doctor.js +51 -0
- package/dist/commands/recovery-checkpoints.d.ts +164 -0
- package/dist/commands/recovery-checkpoints.js +347 -0
- package/dist/commands/revoke.js +19 -7
- package/dist/commands/status.js +7 -0
- package/dist/config-io.js +49 -18
- package/dist/conformance/guarantee-table.js +6 -0
- package/dist/conformance/types.d.ts +1 -0
- package/dist/core/approval-replay.d.ts +1 -1
- package/dist/core/approval-replay.js +4 -1
- package/dist/core/approval-service.d.ts +6 -1
- package/dist/core/approval-service.js +86 -27
- package/dist/core/approval.d.ts +2 -0
- package/dist/core/approval.js +6 -3
- package/dist/core/audit-types.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.d.ts +19 -0
- package/dist/core/capability/approval-state-mutation.js +76 -3
- package/dist/core/capability/approval-v3.d.ts +18 -1
- package/dist/core/capability/approval-v3.js +63 -23
- package/dist/core/capability/attestation.d.ts +3 -0
- package/dist/core/capability/attestation.js +7 -0
- package/dist/core/capability/boundary-driver-container.d.ts +12 -0
- package/dist/core/capability/boundary-driver-container.js +101 -34
- package/dist/core/capability/boundary-driver.js +1 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +1 -0
- package/dist/core/capability/boundary-grant-materialize.js +15 -0
- package/dist/core/capability/boundary-run.d.ts +25 -0
- package/dist/core/capability/boundary-run.js +37 -9
- package/dist/core/capability/boundary-session.d.ts +4 -0
- package/dist/core/capability/boundary-session.js +9 -0
- package/dist/core/capability/boundary-workspace-mount.d.ts +5 -0
- package/dist/core/capability/boundary-workspace-mount.js +68 -0
- package/dist/core/capability/gate-policy-shadow.js +23 -1
- package/dist/core/capability/grant-consumption.d.ts +4 -0
- package/dist/core/capability/grant-consumption.js +11 -0
- package/dist/core/capability/grant-lease.d.ts +36 -1
- package/dist/core/capability/grant-lease.js +213 -16
- package/dist/core/capability/grant-match.js +3 -0
- package/dist/core/capability/index.d.ts +1 -1
- package/dist/core/capability/index.js +1 -1
- package/dist/core/capability/policy-engine.d.ts +7 -0
- package/dist/core/capability/policy-engine.js +202 -15
- package/dist/core/capability/request.d.ts +3 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.js +62 -0
- package/dist/core/effect-ir/audit.d.ts +9 -0
- package/dist/core/effect-ir/audit.js +86 -0
- package/dist/core/effect-ir/build.d.ts +31 -0
- package/dist/core/effect-ir/build.js +277 -0
- package/dist/core/effect-ir/index.d.ts +8 -0
- package/dist/core/effect-ir/index.js +7 -0
- package/dist/core/effect-ir/normalize.d.ts +4 -0
- package/dist/core/effect-ir/normalize.js +104 -0
- package/dist/core/effect-ir/package-exec-eval.d.ts +8 -0
- package/dist/core/effect-ir/package-exec-eval.js +152 -0
- package/dist/core/effect-ir/package-exec.d.ts +29 -0
- package/dist/core/effect-ir/package-exec.js +292 -0
- package/dist/core/effect-ir/policy.d.ts +13 -0
- package/dist/core/effect-ir/policy.js +77 -0
- package/dist/core/effect-ir/types.d.ts +55 -0
- package/dist/core/effect-ir/types.js +1 -0
- package/dist/core/egress/allowlist.d.ts +1 -0
- package/dist/core/egress/allowlist.js +8 -0
- package/dist/core/egress-approval.js +45 -51
- package/dist/core/gate-contract.d.ts +2 -0
- package/dist/core/gate-contract.js +2 -0
- package/dist/core/gate-engine.js +26 -9
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +2 -1
- package/dist/core/judge-cloud-consent.js +29 -15
- package/dist/core/network-endpoint.d.ts +10 -0
- package/dist/core/network-endpoint.js +51 -0
- package/dist/core/process-runner.d.ts +10 -0
- package/dist/core/process-runner.js +124 -0
- package/dist/core/recover-advice.js +2 -1
- package/dist/core/recovery/artifact-store.d.ts +29 -0
- package/dist/core/recovery/artifact-store.js +336 -0
- package/dist/core/recovery/checkpoint.d.ts +25 -0
- package/dist/core/recovery/checkpoint.js +260 -0
- package/dist/core/recovery/index.d.ts +3 -1
- package/dist/core/recovery/index.js +2 -0
- package/dist/core/recovery/operator-guidance.d.ts +7 -0
- package/dist/core/recovery/operator-guidance.js +39 -0
- package/dist/core/recovery/reconcile.d.ts +3 -0
- package/dist/core/recovery/reconcile.js +48 -0
- package/dist/core/recovery/resource-identity.d.ts +4 -0
- package/dist/core/recovery/resource-identity.js +34 -0
- package/dist/core/recovery/restore.d.ts +13 -0
- package/dist/core/recovery/restore.js +114 -0
- package/dist/core/recovery/snapshot-node.d.ts +27 -0
- package/dist/core/recovery/snapshot-node.js +261 -0
- package/dist/core/recovery/types.d.ts +85 -1
- package/dist/core/transactional/apply-observed-changes.d.ts +13 -0
- package/dist/core/transactional/apply-observed-changes.js +275 -0
- package/dist/core/transactional/backend-selector.d.ts +8 -0
- package/dist/core/transactional/backend-selector.js +118 -0
- package/dist/core/transactional/backend.d.ts +55 -0
- package/dist/core/transactional/backend.js +1 -0
- package/dist/core/transactional/file-checkpoint-backend.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-backend.js +278 -0
- package/dist/core/transactional/file-checkpoint-git.d.ts +11 -0
- package/dist/core/transactional/file-checkpoint-git.js +217 -0
- package/dist/core/transactional/file-checkpoint-staging.d.ts +13 -0
- package/dist/core/transactional/file-checkpoint-staging.js +54 -0
- package/dist/core/transactional/file-clone.d.ts +15 -0
- package/dist/core/transactional/file-clone.js +139 -0
- package/dist/core/transactional/file-tree-path.d.ts +7 -0
- package/dist/core/transactional/file-tree-path.js +50 -0
- package/dist/core/transactional/file-tree.d.ts +41 -0
- package/dist/core/transactional/file-tree.js +186 -0
- package/dist/core/transactional/git-worktree-backend.d.ts +2 -0
- package/dist/core/transactional/git-worktree-backend.js +50 -0
- package/dist/core/transactional/git-worktree.d.ts +9 -11
- package/dist/core/transactional/git-worktree.js +24 -119
- package/dist/core/transactional/index.d.ts +11 -0
- package/dist/core/transactional/index.js +10 -0
- package/dist/core/transactional/reasons.js +1 -0
- package/dist/core/transactional/runner.js +157 -25
- package/dist/core/transactional/snapshot-node.d.ts +26 -0
- package/dist/core/transactional/snapshot-node.js +87 -0
- package/dist/core/transactional/types.d.ts +17 -1
- package/dist/core/types.d.ts +10 -1
- package/dist/core/verdict/adapter.js +8 -0
- package/dist/core/verdict/launcher-resolve.js +2 -20
- package/dist/core/verdict/shell-policy.d.ts +1 -0
- package/dist/core/verdict/shell-policy.js +1 -1
- package/dist/core/verdict/types.d.ts +5 -0
- package/dist/core/verdict/verdict.js +37 -20
- package/dist/types.d.ts +11 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/belay/SKILL.md +8 -3
- package/skills/belay/belay-approve.md +10 -7
package/dist/core/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { CapabilityGrantV1 } from './capability/grant.js';
|
|
|
3
3
|
import type { PolicyDecision } from './capability/policy-types.js';
|
|
4
4
|
import type { CapabilityRequestV1 } from './capability/request.js';
|
|
5
5
|
import type { FsScopeAllowlistFile } from './capability/types.js';
|
|
6
|
+
import type { EffectPlan } from './effect-ir/types.js';
|
|
6
7
|
export type BelayMode = 'enforce' | 'audit';
|
|
7
8
|
export type HookVerdict = 'allow' | 'allow_flagged' | 'deny_pending_approval';
|
|
8
9
|
export type Reversibility = 'reversible' | 'recoverable_with_cost' | 'irreversible';
|
|
@@ -40,6 +41,8 @@ export interface ClassifyResult {
|
|
|
40
41
|
axes?: VerdictAxes;
|
|
41
42
|
capabilityRequests?: CapabilityRequestV1[];
|
|
42
43
|
authorizationDecision?: PolicyDecision;
|
|
44
|
+
effectPlan?: EffectPlan;
|
|
45
|
+
effectPlanPolicyDecisions?: PolicyDecision[];
|
|
43
46
|
boundaryProfile?: string;
|
|
44
47
|
}
|
|
45
48
|
export type UnknownLocalEffectPolicy = 'allow_flagged' | 'deny';
|
|
@@ -116,8 +119,14 @@ export interface ApprovalRecord {
|
|
|
116
119
|
capabilityRequests?: CapabilityRequestV1[];
|
|
117
120
|
/** Approval state v3: hash of capabilityRequests for replay binding. */
|
|
118
121
|
capabilityRequestHash?: string;
|
|
119
|
-
/** Approval state v3:
|
|
122
|
+
/** Approval state v3: hash of normalized EffectPlan for composite replay binding. */
|
|
123
|
+
effectPlanHash?: string;
|
|
124
|
+
/** Approval state v3: minted grant after human approval (legacy single grant; first of bundle). */
|
|
120
125
|
grant?: CapabilityGrantV1;
|
|
126
|
+
/** Approval state v3: all grants for composite capability requests (atomic bundle). */
|
|
127
|
+
grants?: CapabilityGrantV1[];
|
|
128
|
+
/** Exact one-to-one grant bundle contract. Missing means legacy fingerprint authorization. */
|
|
129
|
+
grantBundleVersion?: 1;
|
|
121
130
|
}
|
|
122
131
|
export interface ApprovalStateFile {
|
|
123
132
|
version: 1 | 2 | 3;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BOUNDARY_PROFILE_L3_L4_ONLY, resolveBoundaryProfile, } from '../capability/boundary-profile.js';
|
|
2
|
+
import { effectPlanAuditFields } from '../effect-ir/audit.js';
|
|
2
3
|
import { judgeTraceAuditFields } from './judge-audit.js';
|
|
3
4
|
import { recordJudgeLatency } from './judge-baseline.js';
|
|
4
5
|
import { verdict } from './verdict.js';
|
|
@@ -120,6 +121,8 @@ export function verdictToClassifyResult(result, config, options = {}) {
|
|
|
120
121
|
},
|
|
121
122
|
capabilityRequests: result.capabilityRequests,
|
|
122
123
|
authorizationDecision: result.authorizationDecision,
|
|
124
|
+
effectPlan: result.effectPlan,
|
|
125
|
+
effectPlanPolicyDecisions: result.effectPlanPolicyDecisions,
|
|
123
126
|
boundaryProfile: options.boundaryProfile ??
|
|
124
127
|
(config
|
|
125
128
|
? resolveBoundaryProfile({ config, attestation: options.attestation })
|
|
@@ -138,6 +141,11 @@ export function verdictAuditFields(result) {
|
|
|
138
141
|
would: result.permission,
|
|
139
142
|
by: 'verdict',
|
|
140
143
|
signals: result.signals,
|
|
144
|
+
...effectPlanAuditFields(result.effectPlan, {
|
|
145
|
+
capabilityRequests: result.capabilityRequests,
|
|
146
|
+
decisions: result.effectPlanPolicyDecisions,
|
|
147
|
+
authorizationDecision: result.authorizationDecision,
|
|
148
|
+
}),
|
|
141
149
|
...judgeTraceAuditFields(result.judgeTrace),
|
|
142
150
|
};
|
|
143
151
|
}
|
|
@@ -34,17 +34,6 @@ const PNPM_BUILTIN_COMMANDS = new Set([
|
|
|
34
34
|
'up',
|
|
35
35
|
'why',
|
|
36
36
|
]);
|
|
37
|
-
const PNPM_EXEC_LIKE_HEADS = new Set([
|
|
38
|
-
'vitest',
|
|
39
|
-
'vite',
|
|
40
|
-
'biome',
|
|
41
|
-
'eslint',
|
|
42
|
-
'jest',
|
|
43
|
-
'mocha',
|
|
44
|
-
'tsc',
|
|
45
|
-
'tsx',
|
|
46
|
-
'node',
|
|
47
|
-
]);
|
|
48
37
|
function readPackageJson(dir) {
|
|
49
38
|
const packagePath = path.join(dir, 'package.json');
|
|
50
39
|
if (!existsSync(packagePath)) {
|
|
@@ -232,12 +221,12 @@ export function resolveLauncherRecipe(params) {
|
|
|
232
221
|
const resolution = resolveNpmRecipe(params.cwd, params.repoRoot, scriptName, forwardedArgs(tokens));
|
|
233
222
|
if (tokens[0] === 'pnpm' &&
|
|
234
223
|
tokens[1] &&
|
|
235
|
-
|
|
224
|
+
!PNPM_BUILTIN_COMMANDS.has(tokens[1]) &&
|
|
236
225
|
resolution.reason === 'npm_script_undefined') {
|
|
237
226
|
return {
|
|
238
227
|
recipes: [tokens.slice(1).join(' ')],
|
|
239
228
|
opaque: false,
|
|
240
|
-
reason: '
|
|
229
|
+
reason: 'pnpm_exec_shorthand',
|
|
241
230
|
};
|
|
242
231
|
}
|
|
243
232
|
return resolution;
|
|
@@ -252,13 +241,6 @@ export function resolveLauncherRecipe(params) {
|
|
|
252
241
|
reason: 'pnpm_exec',
|
|
253
242
|
};
|
|
254
243
|
}
|
|
255
|
-
if (tokens[0] === 'pnpm' && tokens[1] && PNPM_EXEC_LIKE_HEADS.has(tokens[1])) {
|
|
256
|
-
return {
|
|
257
|
-
recipes: [tokens.slice(1).join(' ')],
|
|
258
|
-
opaque: false,
|
|
259
|
-
reason: 'pnpm_exec_like',
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
244
|
return null;
|
|
263
245
|
}
|
|
264
246
|
const READ_ONLY_LAUNCHER_SUFFIXES = new Set(['--version', '-v', '--help', '-h']);
|
|
@@ -16,6 +16,7 @@ export interface SegmentShellPolicyInput {
|
|
|
16
16
|
}
|
|
17
17
|
export declare function evaluateSegmentShellPolicy(input: SegmentShellPolicyInput): {
|
|
18
18
|
request: CapabilityRequestV1;
|
|
19
|
+
requests: CapabilityRequestV1[];
|
|
19
20
|
decision: PolicyDecision;
|
|
20
21
|
};
|
|
21
22
|
/** Attach capability metadata without influencing the verdict (audit / shadow only). */
|
|
@@ -34,7 +34,7 @@ export function evaluateSegmentShellPolicy(input) {
|
|
|
34
34
|
export function shellPolicyMetadata(input) {
|
|
35
35
|
const result = evaluateSegmentShellPolicy(input);
|
|
36
36
|
return {
|
|
37
|
-
capabilityRequests:
|
|
37
|
+
capabilityRequests: result.requests,
|
|
38
38
|
authorizationDecision: result.decision,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PolicyDecision } from '../capability/policy-types.js';
|
|
2
2
|
import type { CapabilityRequestV1 } from '../capability/request.js';
|
|
3
3
|
import type { BelayConfigV4 } from '../config.js';
|
|
4
|
+
import type { EffectPlan } from '../effect-ir/types.js';
|
|
4
5
|
export type VerdictPermission = 'allow' | 'ask';
|
|
5
6
|
export type VerdictLocation = 'repo_local' | 'repo_outside' | 'external' | 'mixed' | 'unknown';
|
|
6
7
|
export type VerdictOpacity = 'transparent' | 'recursive' | 'opaque' | 'unparseable';
|
|
@@ -19,6 +20,8 @@ export interface VerdictResult {
|
|
|
19
20
|
judgeTrace?: JudgeTrace;
|
|
20
21
|
capabilityRequests?: CapabilityRequestV1[];
|
|
21
22
|
authorizationDecision?: PolicyDecision;
|
|
23
|
+
effectPlan?: EffectPlan;
|
|
24
|
+
effectPlanPolicyDecisions?: PolicyDecision[];
|
|
22
25
|
}
|
|
23
26
|
export interface Tier1Verdict {
|
|
24
27
|
/** true = undoable via git/fs snapshot or trivial local revert (ADR-002 local-recoverable). */
|
|
@@ -93,4 +96,6 @@ export interface InternalSegmentVerdict {
|
|
|
93
96
|
judgeTrace?: JudgeTrace;
|
|
94
97
|
capabilityRequests?: CapabilityRequestV1[];
|
|
95
98
|
authorizationDecision?: PolicyDecision;
|
|
99
|
+
effectPlan?: EffectPlan;
|
|
100
|
+
effectPlanPolicyDecisions?: PolicyDecision[];
|
|
96
101
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { policyDecisionToLegacyReason } from '../capability/policy-bridge.js';
|
|
3
|
-
import { policyDecisionRequiresAsk } from '../capability/policy-engine.js';
|
|
3
|
+
import { combinePolicyDecisions, policyDecisionRequiresAsk } from '../capability/policy-engine.js';
|
|
4
|
+
import { mergeEffectPlans } from '../effect-ir/normalize.js';
|
|
5
|
+
import { isPackageExecLauncher } from '../effect-ir/package-exec.js';
|
|
6
|
+
import { evaluatePackageExecSegment } from '../effect-ir/package-exec-eval.js';
|
|
4
7
|
import { canonicalPath, resolveMutationTarget } from '../path-utils.js';
|
|
5
8
|
import { extractRedirectTargets, isFdDuplication, isRedirectOperator, tokenizeShell, } from '../shell-tokenizer.js';
|
|
6
9
|
import { analyzePathTargets, cwdRelative, isDestructiveHighStakesMutation, resolveTrustedPath, touchesProtectedRoot, } from './containment.js';
|
|
@@ -94,6 +97,12 @@ function mergeLocation(left, right) {
|
|
|
94
97
|
return 'mixed';
|
|
95
98
|
}
|
|
96
99
|
function combineInternal(left, right) {
|
|
100
|
+
const policyDecisions = [
|
|
101
|
+
...(left.effectPlanPolicyDecisions ??
|
|
102
|
+
(left.authorizationDecision ? [left.authorizationDecision] : [])),
|
|
103
|
+
...(right.effectPlanPolicyDecisions ??
|
|
104
|
+
(right.authorizationDecision ? [right.authorizationDecision] : [])),
|
|
105
|
+
];
|
|
97
106
|
return {
|
|
98
107
|
permission: worsePermission(left.permission, right.permission),
|
|
99
108
|
location: mergeLocation(left.location, right.location),
|
|
@@ -122,11 +131,9 @@ function combineInternal(left, right) {
|
|
|
122
131
|
signals: [...new Set([...left.signals, ...right.signals])],
|
|
123
132
|
judgeTrace: right.judgeTrace ?? left.judgeTrace,
|
|
124
133
|
capabilityRequests: [...(left.capabilityRequests ?? []), ...(right.capabilityRequests ?? [])],
|
|
125
|
-
authorizationDecision:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
: left.authorizationDecision
|
|
129
|
-
: (right.authorizationDecision ?? left.authorizationDecision),
|
|
134
|
+
authorizationDecision: policyDecisions.length > 0 ? combinePolicyDecisions(policyDecisions) : undefined,
|
|
135
|
+
effectPlan: mergeEffectPlans(left.effectPlan, right.effectPlan),
|
|
136
|
+
effectPlanPolicyDecisions: policyDecisions,
|
|
130
137
|
};
|
|
131
138
|
}
|
|
132
139
|
function askVerdict(params) {
|
|
@@ -327,7 +334,7 @@ function tier0HighStakesRm(tokens, context) {
|
|
|
327
334
|
return null;
|
|
328
335
|
}
|
|
329
336
|
function verdictFromOpacityPolicy(params) {
|
|
330
|
-
const {
|
|
337
|
+
const { requests, decision } = evaluateSegmentShellPolicy({
|
|
331
338
|
command: params.command,
|
|
332
339
|
segmentHead: params.segmentHead,
|
|
333
340
|
effect: params.effect,
|
|
@@ -343,7 +350,7 @@ function verdictFromOpacityPolicy(params) {
|
|
|
343
350
|
effect: params.effect,
|
|
344
351
|
confidence: 'deterministic',
|
|
345
352
|
signals: [...params.signals, ...decision.signals],
|
|
346
|
-
capabilityRequests:
|
|
353
|
+
capabilityRequests: requests,
|
|
347
354
|
authorizationDecision: decision,
|
|
348
355
|
};
|
|
349
356
|
if (policyDecisionRequiresAsk(decision)) {
|
|
@@ -428,7 +435,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
428
435
|
if (recursiveScript) {
|
|
429
436
|
const prescan = prescanInterpreterCode(recursiveScript);
|
|
430
437
|
if (prescan && tier1RequiresAsk(prescan)) {
|
|
431
|
-
const {
|
|
438
|
+
const { requests, decision } = evaluateSegmentShellPolicy({
|
|
432
439
|
command: recursiveScript,
|
|
433
440
|
segmentHead: segment.head,
|
|
434
441
|
effect: 'unknown',
|
|
@@ -448,7 +455,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
448
455
|
confidence: 'deterministic',
|
|
449
456
|
reason: legacyReason,
|
|
450
457
|
signals: ['interpreter_secret_prescan', prescan.reason, ...decision.signals],
|
|
451
|
-
capabilityRequests:
|
|
458
|
+
capabilityRequests: requests,
|
|
452
459
|
authorizationDecision: decision,
|
|
453
460
|
});
|
|
454
461
|
}
|
|
@@ -465,6 +472,14 @@ async function evaluateSegment(command, context, depth) {
|
|
|
465
472
|
signals: [...innerVerdict.signals, 'recursive_wrapper'],
|
|
466
473
|
};
|
|
467
474
|
}
|
|
475
|
+
if (isPackageExecLauncher(peeled)) {
|
|
476
|
+
return evaluatePackageExecSegment({
|
|
477
|
+
command,
|
|
478
|
+
peeled,
|
|
479
|
+
context,
|
|
480
|
+
evaluateInner: (innerRecipe) => evaluateSegment(innerRecipe, context, depth + 1),
|
|
481
|
+
});
|
|
482
|
+
}
|
|
468
483
|
if (isRoutineLauncher(peeled)) {
|
|
469
484
|
const resolution = resolveLauncherRecipe({
|
|
470
485
|
tokens: peeled,
|
|
@@ -518,7 +533,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
518
533
|
const semantics = analyzeShellCommandSemantics(peeled, segment, context.cwd);
|
|
519
534
|
const pathContext = pathContextFromSemantics(context, semantics);
|
|
520
535
|
if (semantics.requiresAsk) {
|
|
521
|
-
const {
|
|
536
|
+
const { requests, decision } = evaluateSegmentShellPolicy({
|
|
522
537
|
command,
|
|
523
538
|
segmentHead: segment.head,
|
|
524
539
|
effect: 'local_mutation',
|
|
@@ -535,13 +550,13 @@ async function evaluateSegment(command, context, depth) {
|
|
|
535
550
|
confidence: 'deterministic',
|
|
536
551
|
reason: semantics.requiresAsk.reason,
|
|
537
552
|
signals: semantics.requiresAsk.signals,
|
|
538
|
-
capabilityRequests:
|
|
553
|
+
capabilityRequests: requests,
|
|
539
554
|
authorizationDecision: decision,
|
|
540
555
|
});
|
|
541
556
|
}
|
|
542
557
|
const egressClass = semantics.egressClass ?? classifyEgressTool(segment.head, peeled);
|
|
543
558
|
if (egressClass === 'destructive') {
|
|
544
|
-
const {
|
|
559
|
+
const { requests, decision } = evaluateSegmentShellPolicy({
|
|
545
560
|
command,
|
|
546
561
|
segmentHead: segment.head,
|
|
547
562
|
effect: 'remote_mutation',
|
|
@@ -559,13 +574,13 @@ async function evaluateSegment(command, context, depth) {
|
|
|
559
574
|
confidence: 'deterministic',
|
|
560
575
|
reason: 'tier0_external',
|
|
561
576
|
signals: ['tier0_external', segment.head, ...decision.signals],
|
|
562
|
-
capabilityRequests:
|
|
577
|
+
capabilityRequests: requests,
|
|
563
578
|
authorizationDecision: decision,
|
|
564
579
|
});
|
|
565
580
|
}
|
|
566
581
|
if (tier0ExternalMatch(semantics.normalizedKey ?? segment.key, segment.head, peeled, semantics.signals)) {
|
|
567
582
|
const tier0Key = semantics.normalizedKey ?? segment.key;
|
|
568
|
-
const {
|
|
583
|
+
const { requests, decision } = evaluateSegmentShellPolicy({
|
|
569
584
|
command,
|
|
570
585
|
segmentHead: segment.head,
|
|
571
586
|
effect: 'remote_mutation',
|
|
@@ -582,7 +597,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
582
597
|
confidence: 'deterministic',
|
|
583
598
|
reason: 'tier0_external',
|
|
584
599
|
signals: ['tier0_external', tier0Key, ...decision.signals],
|
|
585
|
-
capabilityRequests:
|
|
600
|
+
capabilityRequests: requests,
|
|
586
601
|
authorizationDecision: decision,
|
|
587
602
|
});
|
|
588
603
|
}
|
|
@@ -743,7 +758,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
743
758
|
})
|
|
744
759
|
: null;
|
|
745
760
|
if (mutationPrescan) {
|
|
746
|
-
const {
|
|
761
|
+
const { requests, decision } = evaluateSegmentShellPolicy(buildPolicyInput({
|
|
747
762
|
effect: 'local_mutation',
|
|
748
763
|
location: pathAnalysis.location === 'unknown'
|
|
749
764
|
? 'unknown'
|
|
@@ -766,7 +781,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
766
781
|
confidence: 'deterministic',
|
|
767
782
|
reason: legacyReason,
|
|
768
783
|
signals: ['tier1_catastrophic', mutationPrescan.reason, ...decision.signals],
|
|
769
|
-
capabilityRequests:
|
|
784
|
+
capabilityRequests: requests,
|
|
770
785
|
authorizationDecision: decision,
|
|
771
786
|
});
|
|
772
787
|
}
|
|
@@ -777,12 +792,12 @@ async function evaluateSegment(command, context, depth) {
|
|
|
777
792
|
(outsideMutation && effect !== 'read_only');
|
|
778
793
|
let policyMetadata = {};
|
|
779
794
|
if (needsPolicy) {
|
|
780
|
-
const {
|
|
795
|
+
const { requests, decision } = evaluateSegmentShellPolicy(buildPolicyInput({
|
|
781
796
|
effect,
|
|
782
797
|
location: pathAnalysis.location,
|
|
783
798
|
}));
|
|
784
799
|
policyMetadata = {
|
|
785
|
-
capabilityRequests:
|
|
800
|
+
capabilityRequests: requests,
|
|
786
801
|
authorizationDecision: decision,
|
|
787
802
|
};
|
|
788
803
|
if (policyDecisionRequiresAsk(decision)) {
|
|
@@ -908,6 +923,8 @@ function toVerdictResult(internal, command, context, fingerprintCwd = context.cw
|
|
|
908
923
|
judgeTrace: internal.judgeTrace,
|
|
909
924
|
capabilityRequests: internal.capabilityRequests,
|
|
910
925
|
authorizationDecision: internal.authorizationDecision,
|
|
926
|
+
effectPlan: internal.effectPlan,
|
|
927
|
+
effectPlanPolicyDecisions: internal.effectPlanPolicyDecisions,
|
|
911
928
|
};
|
|
912
929
|
}
|
|
913
930
|
export async function verdict(command, context) {
|
package/dist/types.d.ts
CHANGED
|
@@ -138,6 +138,17 @@ export interface StatusReport {
|
|
|
138
138
|
dogfood: DogfoodStatus;
|
|
139
139
|
health: HealthSnapshot;
|
|
140
140
|
visibility: AuditVisibilityReport;
|
|
141
|
+
fileCheckpoint: {
|
|
142
|
+
enabled: boolean;
|
|
143
|
+
allowNonGit: boolean;
|
|
144
|
+
transactionalEnabled: boolean;
|
|
145
|
+
durableCheckpointEnabled: boolean;
|
|
146
|
+
maxFiles: number;
|
|
147
|
+
maxSourceBytes: number;
|
|
148
|
+
maxWorkspaceBytes: number;
|
|
149
|
+
prepareTimeoutMs: number;
|
|
150
|
+
copyConcurrency: number;
|
|
151
|
+
};
|
|
141
152
|
}
|
|
142
153
|
export interface ReportOptions {
|
|
143
154
|
targetDir?: string;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const PACKAGE_VERSION = "0.7.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/sync-version.mjs — do not edit.
|
|
2
|
-
export const PACKAGE_VERSION = '0.
|
|
2
|
+
export const PACKAGE_VERSION = '0.7.0';
|
package/package.json
CHANGED
package/skills/belay/SKILL.md
CHANGED
|
@@ -37,9 +37,14 @@ setup (or `npx @guilz-dev/belay init` for non-interactive install). Run
|
|
|
37
37
|
## When belay blocks an action
|
|
38
38
|
|
|
39
39
|
1. Read the approval ID in the deny message.
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
42
|
-
|
|
40
|
+
2. In the editor, approve once with `/belay-approve <approval-id>`.
|
|
41
|
+
3. With default `approval.flow: one_step`, editor approval atomically claims the one-shot grant and
|
|
42
|
+
replays the exact denied shell action through the configured boundary driver. No follow-up prompt
|
|
43
|
+
is required. Failed or timed-out replay requires fresh approval. Tool and subagent actions still
|
|
44
|
+
require retrying the original action unchanged.
|
|
45
|
+
|
|
46
|
+
An instruction may follow the approval line in the same prompt. Belay runs the approved shell action
|
|
47
|
+
first and continues the remaining prompt only after replay succeeds.
|
|
43
48
|
|
|
44
49
|
For shell-only CLI replay after approval: `belay approve <approval-id> --replay`.
|
|
45
50
|
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
Run after belay denies a high-risk action and returns an approval ID.
|
|
2
2
|
|
|
3
|
-
```
|
|
4
|
-
belay
|
|
3
|
+
```text
|
|
4
|
+
/belay-approve <approval-id>
|
|
5
5
|
```
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
With default `approval.flow: one_step`, the editor hook atomically claims the one-shot grant and
|
|
8
|
+
immediately replays the exact denied shell action through the configured boundary driver. No
|
|
9
|
+
follow-up prompt is required. Failed or timed-out replay requires fresh approval. Tool and subagent
|
|
10
|
+
approvals still require a manual retry of the original action unchanged.
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
An instruction may follow the approval line in the same prompt. Belay runs the approved shell action
|
|
13
|
+
first and continues the remaining prompt only after replay succeeds.
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
For CLI approval, replay remains explicit:
|
|
13
16
|
|
|
14
17
|
```bash
|
|
15
18
|
belay approve <approval-id> --replay
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
CLI replay claims the one-shot grant before execution. Failure or timeout requires fresh approval.
|
|
19
22
|
|
|
20
23
|
Restore legacy two-step UX in `belay.config.json`:
|
|
21
24
|
|