@guilz-dev/belay 0.1.0 → 0.2.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 +59 -12
- package/dist/adapters/shared/gate-runtime.js +12 -4
- package/dist/bundle/claude-runtime.mjs +155 -36
- package/dist/bundle/codex-runtime.mjs +155 -36
- package/dist/bundle/cursor-runtime.mjs +155 -36
- package/dist/cli.js +15 -4
- package/dist/commands/classify-for-report.js +3 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/explain.js +14 -14
- package/dist/commands/init-wizard.d.ts +5 -0
- package/dist/commands/init-wizard.js +24 -11
- package/dist/commands/recover.js +2 -2
- package/dist/core/approval.d.ts +3 -0
- package/dist/core/approval.js +18 -3
- package/dist/core/audit-query.js +5 -1
- package/dist/core/classify-tool.js +1 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +2 -2
- package/dist/core/gate-contract.d.ts +1 -1
- package/dist/core/gate-contract.js +1 -1
- package/dist/core/gate-engine.js +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/judge-config.d.ts +5 -1
- package/dist/core/judge-config.js +17 -1
- package/dist/core/judge-doctor.js +2 -2
- package/dist/core/types.d.ts +5 -3
- package/dist/core/{v2 → verdict}/adapter.js +9 -3
- package/dist/core/{v2 → verdict}/egress-classify.js +3 -0
- package/dist/core/{v2 → verdict}/judge.js +10 -12
- package/dist/core/{v2 → verdict}/launcher-resolve.js +72 -1
- package/dist/core/{v2 → verdict}/verdict.js +16 -0
- package/dist/corpus/evaluate.js +2 -2
- package/dist/installer.js +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -1
- package/package.json +5 -2
- package/skills/belay/SKILL.md +19 -5
- /package/dist/core/{v2 → verdict}/adapter.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/containment.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/containment.js +0 -0
- /package/dist/core/{v2 → verdict}/egress-classify.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/fingerprint.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/fingerprint.js +0 -0
- /package/dist/core/{v2 → verdict}/index.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/index.js +0 -0
- /package/dist/core/{v2 → verdict}/judge-audit.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/judge-audit.js +0 -0
- /package/dist/core/{v2 → verdict}/judge-factory.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/judge-factory.js +0 -0
- /package/dist/core/{v2 → verdict}/judge-outbound.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/judge-outbound.js +0 -0
- /package/dist/core/{v2 → verdict}/judge.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/launcher-resolve.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/overrides.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/overrides.js +0 -0
- /package/dist/core/{v2 → verdict}/parser.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/parser.js +0 -0
- /package/dist/core/{v2 → verdict}/types.d.ts +0 -0
- /package/dist/core/{v2 → verdict}/types.js +0 -0
- /package/dist/core/{v2 → verdict}/verdict.d.ts +0 -0
|
@@ -86,7 +86,7 @@ var LEGACY_POLICY_V3 = {
|
|
|
86
86
|
fenceWarnThreshold: DEFAULT_FENCE_WARN_THRESHOLD
|
|
87
87
|
};
|
|
88
88
|
var DEFAULT_POLICY_V3 = {
|
|
89
|
-
unknownLocalEffect: "
|
|
89
|
+
unknownLocalEffect: "allow_flagged",
|
|
90
90
|
unparseableShell: "deny",
|
|
91
91
|
codexUnmappedTool: "deny",
|
|
92
92
|
confidenceThresholds: { ...DEFAULT_CONFIDENCE_THRESHOLDS },
|
|
@@ -704,16 +704,25 @@ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from
|
|
|
704
704
|
import path16 from "node:path";
|
|
705
705
|
|
|
706
706
|
// src/core/approval.ts
|
|
707
|
+
var APPROVAL_EXECUTION_LEASE_MS = 6e4;
|
|
707
708
|
function nowIso() {
|
|
708
709
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
709
710
|
}
|
|
710
711
|
function isExpired(approval) {
|
|
711
712
|
return Date.parse(approval.expiresAt) <= Date.now();
|
|
712
713
|
}
|
|
714
|
+
function isExecutionLeaseExpired(approval) {
|
|
715
|
+
if (!approval.executionLeaseExpiresAt) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
return Date.parse(approval.executionLeaseExpiresAt) <= Date.now();
|
|
719
|
+
}
|
|
713
720
|
function compactApprovals(state) {
|
|
714
721
|
return {
|
|
715
722
|
version: state.version,
|
|
716
|
-
approvals: state.approvals.filter(
|
|
723
|
+
approvals: state.approvals.filter(
|
|
724
|
+
(approval) => !isExpired(approval) && !isExecutionLeaseExpired(approval)
|
|
725
|
+
)
|
|
717
726
|
};
|
|
718
727
|
}
|
|
719
728
|
function escapeRegex(value) {
|
|
@@ -722,8 +731,15 @@ function escapeRegex(value) {
|
|
|
722
731
|
}
|
|
723
732
|
function approvalCommandMatch(prompt, tokenPrefix) {
|
|
724
733
|
const escapedPrefix = escapeRegex(tokenPrefix);
|
|
725
|
-
const
|
|
726
|
-
|
|
734
|
+
const linePattern = new RegExp(`^\\s*${escapedPrefix}\\s+(\\S+)\\s*$`, "i");
|
|
735
|
+
for (const line of prompt.split(/\r?\n/)) {
|
|
736
|
+
if (!line.trim()) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
const match = line.match(linePattern);
|
|
740
|
+
return match?.[1] ?? null;
|
|
741
|
+
}
|
|
742
|
+
return null;
|
|
727
743
|
}
|
|
728
744
|
function buildRetryInstruction(tokenPrefix, approvalId) {
|
|
729
745
|
return `To allow the next matching action once, send ${tokenPrefix} ${approvalId} and then retry the original action unchanged.`;
|
|
@@ -1250,7 +1266,7 @@ function classifyResultToGateVerdict(params) {
|
|
|
1250
1266
|
approvalId,
|
|
1251
1267
|
user_message,
|
|
1252
1268
|
agent_message,
|
|
1253
|
-
|
|
1269
|
+
axes: result.axes
|
|
1254
1270
|
};
|
|
1255
1271
|
}
|
|
1256
1272
|
function unnormalizedGateVerdict(params) {
|
|
@@ -1504,7 +1520,7 @@ function matchesSensitivePath(filePath, patterns) {
|
|
|
1504
1520
|
return false;
|
|
1505
1521
|
}
|
|
1506
1522
|
|
|
1507
|
-
// src/core/
|
|
1523
|
+
// src/core/verdict/judge-audit.ts
|
|
1508
1524
|
function judgeTraceAuditFields(trace) {
|
|
1509
1525
|
if (!trace) {
|
|
1510
1526
|
return {};
|
|
@@ -1519,7 +1535,7 @@ function judgeTraceAuditFields(trace) {
|
|
|
1519
1535
|
};
|
|
1520
1536
|
}
|
|
1521
1537
|
|
|
1522
|
-
// src/core/
|
|
1538
|
+
// src/core/verdict/judge-outbound.ts
|
|
1523
1539
|
var PATH_LIKE = /(?:^|[\s"'`=])(~\/[^\s"'`]+|\/[^\s"'`]+|\.\/[^\s"'`]+|\.\.\/[^\s"'`]+|[A-Za-z]:\\[^\s"'`]+)/g;
|
|
1524
1540
|
var REDACTED_PLACEHOLDER = /^(?:<redacted>|\[REDACTED\]|<secret>|<high-entropy>|<approval-id>)$/i;
|
|
1525
1541
|
var URL_CREDENTIALS_PATTERN2 = /\b[A-Za-z][A-Za-z0-9+.-]*:\/\/([^/\s:@]+):([^@\s/]+)@/gi;
|
|
@@ -1588,16 +1604,16 @@ function scrubOutboundForJudge(text, options) {
|
|
|
1588
1604
|
}
|
|
1589
1605
|
}
|
|
1590
1606
|
|
|
1591
|
-
// src/core/
|
|
1607
|
+
// src/core/verdict/judge.ts
|
|
1592
1608
|
var SECRET_PATTERNS = [/\.env\b/i, /\.pem\b/i, /id_rsa\b/i, /credentials/i, /secrets?\b/i];
|
|
1593
1609
|
var DESTRUCTIVE_VERBS = /\b(rm|rmtree|unlink|delete|truncate|shred|destroy|drop)\b/i;
|
|
1594
1610
|
var GIT_PATTERNS = /\.git\b/i;
|
|
1595
|
-
var TIER1_PROMPT = `Answer ONLY with JSON: {"external_change":boolean,"
|
|
1596
|
-
|
|
1611
|
+
var TIER1_PROMPT = `Answer ONLY with JSON: {"external_change":boolean,"reason":string}
|
|
1612
|
+
reason should be a short snake_case label.
|
|
1597
1613
|
|
|
1598
|
-
external_change:
|
|
1599
|
-
|
|
1600
|
-
|
|
1614
|
+
external_change: true if this operation CREATEs, MODIFYs, DELETEs, or SENDs data to any system outside the local machine and its git repository working tree.
|
|
1615
|
+
READ/QUERY/GET/list/describe/SELECT/HTTP GET is NOT a change. Local build, test, and package install inside the repo are NOT external changes.
|
|
1616
|
+
If unsure, answer true.
|
|
1601
1617
|
|
|
1602
1618
|
Command/code:
|
|
1603
1619
|
`;
|
|
@@ -1612,13 +1628,13 @@ function failClosedVerdict(reason) {
|
|
|
1612
1628
|
function parseTier1Json(raw) {
|
|
1613
1629
|
try {
|
|
1614
1630
|
const parsed = JSON.parse(raw);
|
|
1615
|
-
if (typeof parsed.external_change !== "boolean"
|
|
1631
|
+
if (typeof parsed.external_change !== "boolean") {
|
|
1616
1632
|
return null;
|
|
1617
1633
|
}
|
|
1618
1634
|
return {
|
|
1619
|
-
external_change: parsed.external_change
|
|
1620
|
-
destroys_outside_repo:
|
|
1621
|
-
destroys_history_or_secrets:
|
|
1635
|
+
external_change: parsed.external_change,
|
|
1636
|
+
destroys_outside_repo: false,
|
|
1637
|
+
destroys_history_or_secrets: false,
|
|
1622
1638
|
reason: typeof parsed.reason === "string" ? parsed.reason : "tier1_llm"
|
|
1623
1639
|
};
|
|
1624
1640
|
} catch {
|
|
@@ -1841,10 +1857,10 @@ function createOpenAiCompatibleJudge(options) {
|
|
|
1841
1857
|
return judge;
|
|
1842
1858
|
}
|
|
1843
1859
|
function tier1RequiresAsk(verdict2) {
|
|
1844
|
-
return verdict2.external_change || verdict2.
|
|
1860
|
+
return verdict2.external_change || verdict2.destroys_history_or_secrets;
|
|
1845
1861
|
}
|
|
1846
1862
|
|
|
1847
|
-
// src/core/
|
|
1863
|
+
// src/core/verdict/judge-factory.ts
|
|
1848
1864
|
var FIXTURE_MODELS_URL = new URL("../../../fixtures/judge-models.json", import.meta.url);
|
|
1849
1865
|
function resolveCloudModel(requested, pinned) {
|
|
1850
1866
|
if (requested === "auto") {
|
|
@@ -1891,10 +1907,10 @@ function createJudgeFromConfig(config, options = {}) {
|
|
|
1891
1907
|
return createDeterministicJudgeStub();
|
|
1892
1908
|
}
|
|
1893
1909
|
|
|
1894
|
-
// src/core/
|
|
1910
|
+
// src/core/verdict/verdict.ts
|
|
1895
1911
|
import path11 from "node:path";
|
|
1896
1912
|
|
|
1897
|
-
// src/core/
|
|
1913
|
+
// src/core/verdict/containment.ts
|
|
1898
1914
|
import path8 from "node:path";
|
|
1899
1915
|
function expandHome(token) {
|
|
1900
1916
|
if (token === "~" || token.startsWith("~/")) {
|
|
@@ -1986,7 +2002,7 @@ function cwdRelative(repoRoot, cwd) {
|
|
|
1986
2002
|
return relativeWithinRepo(repoRoot, cwd) ?? cwd;
|
|
1987
2003
|
}
|
|
1988
2004
|
|
|
1989
|
-
// src/core/
|
|
2005
|
+
// src/core/verdict/egress-classify.ts
|
|
1990
2006
|
var EGRESS_TOOL_HEADS = /* @__PURE__ */ new Set([
|
|
1991
2007
|
"aws",
|
|
1992
2008
|
"curl",
|
|
@@ -2084,6 +2100,9 @@ function classifyAws(tokens) {
|
|
|
2084
2100
|
if (/\bs3\s+rm\b/.test(joined)) {
|
|
2085
2101
|
return "destructive";
|
|
2086
2102
|
}
|
|
2103
|
+
if (/\bs3\s+mb\b/.test(joined)) {
|
|
2104
|
+
return "destructive";
|
|
2105
|
+
}
|
|
2087
2106
|
if (/\bs3\s+sync\b/.test(joined)) {
|
|
2088
2107
|
return "destructive";
|
|
2089
2108
|
}
|
|
@@ -2194,15 +2213,59 @@ function classifyNetlify(tokens) {
|
|
|
2194
2213
|
return "ambiguous";
|
|
2195
2214
|
}
|
|
2196
2215
|
|
|
2197
|
-
// src/core/
|
|
2216
|
+
// src/core/verdict/fingerprint.ts
|
|
2198
2217
|
function verdictFingerprint(cwdRelative2, commandRedacted) {
|
|
2199
2218
|
return hashValue(`v2:${cwdRelative2}:${commandRedacted}`);
|
|
2200
2219
|
}
|
|
2201
2220
|
|
|
2202
|
-
// src/core/
|
|
2221
|
+
// src/core/verdict/launcher-resolve.ts
|
|
2203
2222
|
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "node:fs";
|
|
2204
2223
|
import path9 from "node:path";
|
|
2205
2224
|
var MAX_RESOLVE_DEPTH = 8;
|
|
2225
|
+
var PNPM_BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
2226
|
+
"add",
|
|
2227
|
+
"audit",
|
|
2228
|
+
"cache",
|
|
2229
|
+
"config",
|
|
2230
|
+
"deploy",
|
|
2231
|
+
"dlx",
|
|
2232
|
+
"exec",
|
|
2233
|
+
"fetch",
|
|
2234
|
+
"help",
|
|
2235
|
+
"import",
|
|
2236
|
+
"init",
|
|
2237
|
+
"install",
|
|
2238
|
+
"i",
|
|
2239
|
+
"licenses",
|
|
2240
|
+
"link",
|
|
2241
|
+
"list",
|
|
2242
|
+
"outdated",
|
|
2243
|
+
"pack",
|
|
2244
|
+
"patch",
|
|
2245
|
+
"patch-commit",
|
|
2246
|
+
"patch-remove",
|
|
2247
|
+
"publish",
|
|
2248
|
+
"prune",
|
|
2249
|
+
"rebuild",
|
|
2250
|
+
"remove",
|
|
2251
|
+
"rm",
|
|
2252
|
+
"store",
|
|
2253
|
+
"unlink",
|
|
2254
|
+
"update",
|
|
2255
|
+
"up",
|
|
2256
|
+
"why"
|
|
2257
|
+
]);
|
|
2258
|
+
var PNPM_EXEC_LIKE_HEADS = /* @__PURE__ */ new Set([
|
|
2259
|
+
"vitest",
|
|
2260
|
+
"vite",
|
|
2261
|
+
"biome",
|
|
2262
|
+
"eslint",
|
|
2263
|
+
"jest",
|
|
2264
|
+
"mocha",
|
|
2265
|
+
"tsc",
|
|
2266
|
+
"tsx",
|
|
2267
|
+
"node"
|
|
2268
|
+
]);
|
|
2206
2269
|
function readPackageJson(dir) {
|
|
2207
2270
|
const packagePath = path9.join(dir, "package.json");
|
|
2208
2271
|
if (!existsSync4(packagePath)) {
|
|
@@ -2257,6 +2320,12 @@ function npmScriptName(tokens) {
|
|
|
2257
2320
|
if (launcher[0] === "pnpm" && launcher[1] === "run" && launcher[2]) {
|
|
2258
2321
|
return launcher[2];
|
|
2259
2322
|
}
|
|
2323
|
+
if (launcher[0] === "pnpm" && launcher[1] === "test") {
|
|
2324
|
+
return "test";
|
|
2325
|
+
}
|
|
2326
|
+
if (launcher[0] === "pnpm" && launcher[1] && !launcher[1].startsWith("-") && !PNPM_BUILTIN_COMMANDS.has(launcher[1])) {
|
|
2327
|
+
return launcher[1];
|
|
2328
|
+
}
|
|
2260
2329
|
if (launcher[0] === "npm" && launcher[1] && launcher[1] !== "run" && launcher[1] !== "install") {
|
|
2261
2330
|
return null;
|
|
2262
2331
|
}
|
|
@@ -2378,11 +2447,31 @@ function resolveLauncherRecipe(params) {
|
|
|
2378
2447
|
const tokens = params.tokens;
|
|
2379
2448
|
const scriptName = npmScriptName(tokens);
|
|
2380
2449
|
if (scriptName) {
|
|
2381
|
-
|
|
2450
|
+
const resolution = resolveNpmRecipe(
|
|
2451
|
+
params.cwd,
|
|
2452
|
+
params.repoRoot,
|
|
2453
|
+
scriptName,
|
|
2454
|
+
forwardedArgs(tokens)
|
|
2455
|
+
);
|
|
2456
|
+
if (tokens[0] === "pnpm" && tokens[1] && PNPM_EXEC_LIKE_HEADS.has(tokens[1]) && resolution.reason === "npm_script_undefined") {
|
|
2457
|
+
return {
|
|
2458
|
+
recipes: [tokens.slice(1).join(" ")],
|
|
2459
|
+
opaque: false,
|
|
2460
|
+
reason: "pnpm_exec_like"
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
return resolution;
|
|
2382
2464
|
}
|
|
2383
2465
|
if (tokens[0] === "make" && tokens[1] && !tokens[1].startsWith("-")) {
|
|
2384
2466
|
return resolveMakeRecipe(params.cwd, params.repoRoot, tokens[1]);
|
|
2385
2467
|
}
|
|
2468
|
+
if (tokens[0] === "pnpm" && tokens[1] && PNPM_EXEC_LIKE_HEADS.has(tokens[1])) {
|
|
2469
|
+
return {
|
|
2470
|
+
recipes: [tokens.slice(1).join(" ")],
|
|
2471
|
+
opaque: false,
|
|
2472
|
+
reason: "pnpm_exec_like"
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2386
2475
|
return null;
|
|
2387
2476
|
}
|
|
2388
2477
|
function isRoutineLauncher(tokens) {
|
|
@@ -2398,7 +2487,7 @@ function matchesCustomCommand(normalizedCommand, key, pattern) {
|
|
|
2398
2487
|
return normalizedCommand === trimmed || key === trimmed;
|
|
2399
2488
|
}
|
|
2400
2489
|
|
|
2401
|
-
// src/core/
|
|
2490
|
+
// src/core/verdict/overrides.ts
|
|
2402
2491
|
function matchesCustomPatterns(command, segment, patterns) {
|
|
2403
2492
|
if (!patterns || patterns.length === 0) {
|
|
2404
2493
|
return false;
|
|
@@ -2437,7 +2526,7 @@ function askFromCustomExternal(opacity) {
|
|
|
2437
2526
|
};
|
|
2438
2527
|
}
|
|
2439
2528
|
|
|
2440
|
-
// src/core/
|
|
2529
|
+
// src/core/verdict/parser.ts
|
|
2441
2530
|
import path10 from "node:path";
|
|
2442
2531
|
|
|
2443
2532
|
// src/core/shell-substitution.ts
|
|
@@ -2661,7 +2750,7 @@ function hasUnbalancedDollarParen(command) {
|
|
|
2661
2750
|
return depth > 0;
|
|
2662
2751
|
}
|
|
2663
2752
|
|
|
2664
|
-
// src/core/
|
|
2753
|
+
// src/core/verdict/parser.ts
|
|
2665
2754
|
var ENV_PREFIX_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|"[^"]*"|\S+)$/;
|
|
2666
2755
|
var TRANSPARENT_WRAPPERS = /* @__PURE__ */ new Set([
|
|
2667
2756
|
"sudo",
|
|
@@ -2861,7 +2950,7 @@ function redactCommand(command) {
|
|
|
2861
2950
|
return command.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]").replace(/sk-[A-Za-z0-9]{8,}/g, "sk-[REDACTED]").trim();
|
|
2862
2951
|
}
|
|
2863
2952
|
|
|
2864
|
-
// src/core/
|
|
2953
|
+
// src/core/verdict/verdict.ts
|
|
2865
2954
|
var DEFAULT_MAX_DEPTH = 8;
|
|
2866
2955
|
var TIER0_EXTERNAL_KEYS = /* @__PURE__ */ new Set([
|
|
2867
2956
|
"git push",
|
|
@@ -2944,6 +3033,7 @@ var LOCAL_ROUTINE_HEADS = /* @__PURE__ */ new Set([
|
|
|
2944
3033
|
"make",
|
|
2945
3034
|
"cmake"
|
|
2946
3035
|
]);
|
|
3036
|
+
var BELAY_SELF_COMMANDS = /* @__PURE__ */ new Set(["approve", "revoke"]);
|
|
2947
3037
|
var FIND_DANGEROUS_FLAGS = /* @__PURE__ */ new Set(["-delete", "-exec", "-execdir", "-ok", "-okdir"]);
|
|
2948
3038
|
function isFindDangerous(tokens) {
|
|
2949
3039
|
return tokens.some(
|
|
@@ -3091,6 +3181,11 @@ function tier0ExternalMatch(key, head, tokens) {
|
|
|
3091
3181
|
}
|
|
3092
3182
|
return false;
|
|
3093
3183
|
}
|
|
3184
|
+
function isBelaySelfCommand(tokens) {
|
|
3185
|
+
const head = tokens[0] ?? "";
|
|
3186
|
+
const subcommand = tokens[1] ?? "";
|
|
3187
|
+
return head === "belay" && BELAY_SELF_COMMANDS.has(subcommand);
|
|
3188
|
+
}
|
|
3094
3189
|
function tier0HighStakesRm(tokens, context) {
|
|
3095
3190
|
const head = tokens[0] ?? "";
|
|
3096
3191
|
if (head !== "rm") {
|
|
@@ -3322,6 +3417,16 @@ async function evaluateSegment(command, context, depth) {
|
|
|
3322
3417
|
if (rmVerdict) {
|
|
3323
3418
|
return rmVerdict;
|
|
3324
3419
|
}
|
|
3420
|
+
if (isBelaySelfCommand(peeled)) {
|
|
3421
|
+
return allowVerdict({
|
|
3422
|
+
location: "unknown",
|
|
3423
|
+
opacity: "transparent",
|
|
3424
|
+
effect: "local_mutation",
|
|
3425
|
+
confidence: "deterministic",
|
|
3426
|
+
reason: "belay_control_plane_command",
|
|
3427
|
+
signals: ["belay_control_plane_command", segment.head]
|
|
3428
|
+
});
|
|
3429
|
+
}
|
|
3325
3430
|
let effect = "unknown";
|
|
3326
3431
|
if (READ_ONLY_KEYS.has(segment.key) || READ_ONLY_KEYS.has(segment.head)) {
|
|
3327
3432
|
effect = "read_only";
|
|
@@ -3542,7 +3647,7 @@ async function verdict(command, context) {
|
|
|
3542
3647
|
);
|
|
3543
3648
|
}
|
|
3544
3649
|
|
|
3545
|
-
// src/core/
|
|
3650
|
+
// src/core/verdict/adapter.ts
|
|
3546
3651
|
function buildVerdictContext(params) {
|
|
3547
3652
|
const protectedArtifactRoots2 = [
|
|
3548
3653
|
...params.options?.protectedArtifactRoots ?? [],
|
|
@@ -3589,6 +3694,12 @@ function mapLegacyReason(result) {
|
|
|
3589
3694
|
if (result.reason === "repo_local_mutation") {
|
|
3590
3695
|
return "local_mutation";
|
|
3591
3696
|
}
|
|
3697
|
+
if (result.reason === "tier1_not_restorable") {
|
|
3698
|
+
return "tier1_catastrophic";
|
|
3699
|
+
}
|
|
3700
|
+
if (result.reason === "tier0_restorable" || result.reason === "tier1_restorable") {
|
|
3701
|
+
return result.effect === "local_mutation" ? "local_mutation" : result.reason;
|
|
3702
|
+
}
|
|
3592
3703
|
return result.reason;
|
|
3593
3704
|
}
|
|
3594
3705
|
function verdictToClassifyResult(result) {
|
|
@@ -3609,13 +3720,13 @@ function verdictToClassifyResult(result) {
|
|
|
3609
3720
|
assessment,
|
|
3610
3721
|
normalizedCommand: result.commandRedacted,
|
|
3611
3722
|
summary: result.commandRedacted,
|
|
3612
|
-
|
|
3723
|
+
axes: {
|
|
3613
3724
|
location: result.location,
|
|
3614
3725
|
opacity: result.opacity,
|
|
3615
3726
|
effect: result.effect,
|
|
3616
3727
|
confidence: result.confidence,
|
|
3617
3728
|
would: result.permission,
|
|
3618
|
-
by: "
|
|
3729
|
+
by: "verdict",
|
|
3619
3730
|
commandRedacted: result.commandRedacted,
|
|
3620
3731
|
commandFingerprint: result.fingerprint,
|
|
3621
3732
|
signals: result.signals,
|
|
@@ -4065,7 +4176,7 @@ function normalizeGatedAction(params) {
|
|
|
4065
4176
|
};
|
|
4066
4177
|
}
|
|
4067
4178
|
function applyShellPeripheralPolicy(command, action, result, options) {
|
|
4068
|
-
if (options.brokerFsScope && result.verdict === "deny_pending_approval" && (result.reason === "outside_repo_mutation" || result.reason === "outside_repo_redirect" || result.reason === "repo_outside_mutation" || result.
|
|
4179
|
+
if (options.brokerFsScope && result.verdict === "deny_pending_approval" && (result.reason === "outside_repo_mutation" || result.reason === "outside_repo_redirect" || result.reason === "repo_outside_mutation" || result.axes?.location === "repo_outside")) {
|
|
4069
4180
|
const outsideRepoPaths = collectOutsideRepoPaths(command, action.cwd, action.repoRoot);
|
|
4070
4181
|
if (outsideRepoPaths.length > 0 && options.fsScopeAllowlist && allPathsAllowlisted(outsideRepoPaths, options.fsScopeAllowlist)) {
|
|
4071
4182
|
return {
|
|
@@ -4744,7 +4855,15 @@ async function consumeApprovedApproval(ctx, deps, kind, fingerprint) {
|
|
|
4744
4855
|
await deps.writeApprovals(approved.filePath, approved.state);
|
|
4745
4856
|
return null;
|
|
4746
4857
|
}
|
|
4747
|
-
const
|
|
4858
|
+
const approval = approved.state.approvals[index];
|
|
4859
|
+
if (approval.executionLeaseExpiresAt) {
|
|
4860
|
+
await deps.writeApprovals(approved.filePath, approved.state);
|
|
4861
|
+
return approval;
|
|
4862
|
+
}
|
|
4863
|
+
approved.state.approvals[index] = {
|
|
4864
|
+
...approval,
|
|
4865
|
+
executionLeaseExpiresAt: new Date(Date.now() + APPROVAL_EXECUTION_LEASE_MS).toISOString()
|
|
4866
|
+
};
|
|
4748
4867
|
await deps.writeApprovals(approved.filePath, approved.state);
|
|
4749
4868
|
return approval;
|
|
4750
4869
|
}
|
|
@@ -4882,8 +5001,8 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
4882
5001
|
predictedAssessment: auditExtras.predictedAssessment,
|
|
4883
5002
|
observedAssessment: auditExtras.observedAssessment,
|
|
4884
5003
|
mode: ctx.config.mode,
|
|
4885
|
-
schemaVersion: result.
|
|
4886
|
-
...result.
|
|
5004
|
+
schemaVersion: result.axes ? 2 : 1,
|
|
5005
|
+
...result.axes ?? {},
|
|
4887
5006
|
...auditExtras.transactionalLayer
|
|
4888
5007
|
};
|
|
4889
5008
|
if (result.reason === TRANSACTIONAL_ALREADY_APPLIED) {
|