@guilz-dev/belay 0.10.0 → 0.10.1

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.
@@ -1847,7 +1847,7 @@ function normalizeConfig(config) {
1847
1847
  policy: {
1848
1848
  unknownLocalEffect: v4.policy?.unknownLocalEffect === "deny" ? "deny" : v4.policy?.unknownLocalEffect === "allow_flagged" ? "allow_flagged" : DEFAULT_POLICY_V3.unknownLocalEffect,
1849
1849
  unparseableShell: v4.policy?.unparseableShell === "deny" ? "deny" : v4.policy?.unparseableShell === "allow_flagged" ? "allow_flagged" : DEFAULT_POLICY_V3.unparseableShell,
1850
- codexUnmappedTool: v4.policy?.codexUnmappedTool === "allow" ? "allow" : "deny",
1850
+ codexUnmappedTool: "allow",
1851
1851
  fenceWarnThreshold: typeof v4.policy?.fenceWarnThreshold === "number" && v4.policy.fenceWarnThreshold > 0 && v4.policy.fenceWarnThreshold <= 1 ? v4.policy.fenceWarnThreshold : DEFAULT_FENCE_WARN_THRESHOLD,
1852
1852
  confidenceThresholds: {
1853
1853
  allow: typeof v4.policy?.confidenceThresholds?.allow === "number" ? v4.policy.confidenceThresholds.allow : DEFAULT_CONFIDENCE_THRESHOLDS.allow,
@@ -2149,7 +2149,7 @@ var init_config = __esm({
2149
2149
  DEFAULT_POLICY_V3 = {
2150
2150
  unknownLocalEffect: "allow_flagged",
2151
2151
  unparseableShell: "deny",
2152
- codexUnmappedTool: "deny",
2152
+ codexUnmappedTool: "allow",
2153
2153
  confidenceThresholds: { ...DEFAULT_CONFIDENCE_THRESHOLDS },
2154
2154
  modelAssist: { ...DEFAULT_MODEL_ASSIST },
2155
2155
  transactional: { ...DEFAULT_TRANSACTIONAL_V3 },
@@ -10874,6 +10874,40 @@ function evaluateFileMutationPolicy(analysis, config, auth) {
10874
10874
  );
10875
10875
  return { request, decision };
10876
10876
  }
10877
+ function buildFileReadCapabilityRequest(analysis) {
10878
+ return {
10879
+ version: CAPABILITY_REQUEST_VERSION,
10880
+ principal: {
10881
+ adapter: analysis.adapter,
10882
+ repoRoot: analysis.repoRoot,
10883
+ sessionHash: hashSession(`${analysis.repoRoot}:${analysis.cwd}`)
10884
+ },
10885
+ action: "fs.read",
10886
+ resource: { kind: "path", path: analysis.resolvedPath },
10887
+ context: {
10888
+ cwd: analysis.cwd,
10889
+ inputFingerprint: analysis.inputFingerprint,
10890
+ hookKind: analysis.hookKind,
10891
+ analysisBasis: [`tool:${analysis.toolKind}`, `location:${analysis.locationLabel}`, "read"]
10892
+ },
10893
+ evidence: {
10894
+ level: analysis.locationLabel === "repo_local" ? "certain" : "possible",
10895
+ signals: [...analysis.signals, "effect.fs_read"]
10896
+ }
10897
+ };
10898
+ }
10899
+ function evaluateFileReadPolicy(analysis, config, auth) {
10900
+ const request = buildFileReadCapabilityRequest(analysis);
10901
+ const enriched = enrichAuthWithMaterializedGrants(request, config, {
10902
+ ...auth,
10903
+ sensitivePaths: auth?.sensitivePaths ?? analysis.sensitivePaths
10904
+ });
10905
+ const decision = getDefaultPolicyEngine().evaluate(
10906
+ request,
10907
+ buildAuthorizationContext(config, analysis.trustedWorkspaceRoots, enriched)
10908
+ );
10909
+ return { request, decision };
10910
+ }
10877
10911
 
10878
10912
  // src/core/capability/reasons.ts
10879
10913
  var FS_SCOPE_REASONS = /* @__PURE__ */ new Set(["outside_repo_mutation", "outside_repo_redirect"]);
@@ -17144,17 +17178,41 @@ function verdictToClassifyResult(result, config, options = {}) {
17144
17178
 
17145
17179
  // src/core/classify-tool.ts
17146
17180
  var DEFAULT_SENSITIVE_PATHS = [".env", ".env.*", "**/credentials/**"];
17147
- var FILE_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["write"]);
17148
- var FILE_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set([
17149
- "edit",
17150
- "multiedit",
17151
- "multi_edit",
17152
- "patch",
17153
- "strreplace",
17154
- "str_replace"
17155
- ]);
17156
- var FILE_DELETE_TOOL_NAMES = /* @__PURE__ */ new Set(["delete"]);
17157
- var APPLY_PATCH_TOOL_NAMES = /* @__PURE__ */ new Set(["apply_patch", "applypatch"]);
17181
+ function toolInputRecord(payload) {
17182
+ const toolInput = payload.tool_input;
17183
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
17184
+ return null;
17185
+ }
17186
+ return toolInput;
17187
+ }
17188
+ function hasMutationFields(input) {
17189
+ return typeof input.contents === "string" || typeof input.old_string === "string" || typeof input.new_string === "string" || typeof input.newContents === "string";
17190
+ }
17191
+ function inferToolPayloadEffect(payload, toolName) {
17192
+ if (extractShellCommand(payload)) {
17193
+ return "shell";
17194
+ }
17195
+ const input = toolInputRecord(payload);
17196
+ if (!input) {
17197
+ return "indeterminate";
17198
+ }
17199
+ if (hasMutationFields(input)) {
17200
+ return "file_mutation";
17201
+ }
17202
+ if (extractPatch(payload)) {
17203
+ return "apply_patch";
17204
+ }
17205
+ if (typeof input.pattern === "string" || typeof input.glob_pattern === "string") {
17206
+ return "search_read";
17207
+ }
17208
+ if (typeof input.file_path === "string") {
17209
+ return "file_read";
17210
+ }
17211
+ if (typeof input.path === "string" || typeof input.target_file === "string") {
17212
+ return normalizedToolName(toolName) === "delete" ? "file_delete" : "indeterminate";
17213
+ }
17214
+ return "indeterminate";
17215
+ }
17158
17216
  function policyAuth(options) {
17159
17217
  if (!options.grants && options.attestation === void 0 && options.egressProxyActive === void 0) {
17160
17218
  return void 0;
@@ -17334,6 +17392,314 @@ function classifyFileMutationWithPolicy(params) {
17334
17392
  boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY
17335
17393
  };
17336
17394
  }
17395
+ function classifyFileReadWithPolicy(params) {
17396
+ const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
17397
+ const { request, decision } = evaluateFileReadPolicy(
17398
+ {
17399
+ hookKind: "tool",
17400
+ toolKind: params.toolKind,
17401
+ filePath: params.filePath,
17402
+ resolvedPath: params.resolvedPath,
17403
+ repoRoot: params.repoRoot,
17404
+ cwd: params.cwd,
17405
+ inputFingerprint: fingerprint,
17406
+ signals: params.signals,
17407
+ locationLabel: params.locationLabel,
17408
+ trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
17409
+ sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths
17410
+ },
17411
+ params.config,
17412
+ policyAuth(params.options)
17413
+ );
17414
+ if (policyDecisionRequiresAsk(decision)) {
17415
+ return {
17416
+ verdict: "deny_pending_approval",
17417
+ reason: policyReasonToLegacyReason(decision),
17418
+ summary: params.filePath,
17419
+ fingerprint,
17420
+ assessment: {
17421
+ reversibility: "reversible",
17422
+ external: params.locationLabel === "outside_repo",
17423
+ blastRadius: params.locationLabel === "outside_repo" ? "outside the repository" : "sensitive repository file",
17424
+ confidence: 0.85,
17425
+ signals: [...params.signals, ...decision.signals]
17426
+ },
17427
+ capabilityRequests: [request],
17428
+ authorizationDecision: decision,
17429
+ boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY
17430
+ };
17431
+ }
17432
+ return {
17433
+ verdict: "allow",
17434
+ reason: "effect.fs_read",
17435
+ summary: params.filePath,
17436
+ fingerprint,
17437
+ assessment: {
17438
+ reversibility: "reversible",
17439
+ external: false,
17440
+ blastRadius: "tool scope",
17441
+ confidence: 0.88,
17442
+ signals: [...params.signals, ...decision.signals]
17443
+ },
17444
+ capabilityRequests: [request],
17445
+ authorizationDecision: decision,
17446
+ boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY
17447
+ };
17448
+ }
17449
+ function classifyFilePathMutation(params) {
17450
+ const {
17451
+ toolName,
17452
+ toolKind,
17453
+ filePath,
17454
+ repoRoot,
17455
+ cwd,
17456
+ config,
17457
+ options,
17458
+ sensitivePaths,
17459
+ protectedRoots,
17460
+ isDelete
17461
+ } = params;
17462
+ const signals = [];
17463
+ const resolvedPath = path51.isAbsolute(filePath) ? filePath : path51.resolve(cwd, filePath);
17464
+ const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
17465
+ if (hitsProtectedRoot) {
17466
+ signals.push("control_plane_path");
17467
+ return classifyFileMutationWithPolicy({
17468
+ toolName,
17469
+ toolKind,
17470
+ filePath,
17471
+ resolvedPath,
17472
+ repoRoot,
17473
+ cwd,
17474
+ config,
17475
+ options,
17476
+ signals,
17477
+ isDelete,
17478
+ locationLabel: "control_plane"
17479
+ });
17480
+ }
17481
+ const workspaceMatch = resolveWorkspaceRootMatch(
17482
+ repoRoot,
17483
+ options.trustedWorkspaceRoots,
17484
+ resolvedPath
17485
+ );
17486
+ if (workspaceMatch === null) {
17487
+ signals.push("outside_repo_path");
17488
+ return classifyFileMutationWithPolicy({
17489
+ toolName,
17490
+ toolKind,
17491
+ filePath,
17492
+ resolvedPath,
17493
+ repoRoot,
17494
+ cwd,
17495
+ config,
17496
+ options,
17497
+ signals,
17498
+ isDelete,
17499
+ locationLabel: "outside_repo"
17500
+ });
17501
+ }
17502
+ if (workspaceMatch.kind === "trusted") {
17503
+ signals.push("trusted_workspace_root");
17504
+ }
17505
+ const trustedCwd = resolveClassifierTrustedCwd(cwd, options);
17506
+ const workspacePrescan = mutationPrescanRequiresAsk({
17507
+ targets: [filePath],
17508
+ cwd,
17509
+ repoRoot,
17510
+ trustedCwd,
17511
+ trustedWorkspaceRoots: options.trustedWorkspaceRoots,
17512
+ sensitivePaths
17513
+ });
17514
+ if (workspacePrescan) {
17515
+ return classifyFileMutationWithPolicy({
17516
+ toolName,
17517
+ toolKind,
17518
+ filePath,
17519
+ resolvedPath,
17520
+ repoRoot,
17521
+ cwd,
17522
+ config,
17523
+ options,
17524
+ signals,
17525
+ isDelete,
17526
+ locationLabel: "sensitive_path"
17527
+ });
17528
+ }
17529
+ const relativePath = workspaceMatch.relativePath;
17530
+ if (matchesSensitivePath(relativePath, sensitivePaths)) {
17531
+ signals.push("sensitive_path");
17532
+ return classifyFileMutationWithPolicy({
17533
+ toolName,
17534
+ toolKind,
17535
+ filePath,
17536
+ resolvedPath,
17537
+ repoRoot,
17538
+ cwd,
17539
+ config,
17540
+ options,
17541
+ signals,
17542
+ isDelete,
17543
+ locationLabel: "sensitive_path"
17544
+ });
17545
+ }
17546
+ if (isDelete) {
17547
+ if (isGitPath(resolvedPath, repoRoot)) {
17548
+ signals.push("protected_artifact");
17549
+ return classifyFileMutationWithPolicy({
17550
+ toolName,
17551
+ toolKind,
17552
+ filePath,
17553
+ resolvedPath,
17554
+ repoRoot,
17555
+ cwd,
17556
+ config,
17557
+ options,
17558
+ signals,
17559
+ isDelete: true,
17560
+ locationLabel: "sensitive_path"
17561
+ });
17562
+ }
17563
+ signals.push("file_delete");
17564
+ return classifyFileMutationWithPolicy({
17565
+ toolName,
17566
+ toolKind,
17567
+ filePath,
17568
+ resolvedPath,
17569
+ repoRoot,
17570
+ cwd,
17571
+ config,
17572
+ options,
17573
+ signals,
17574
+ isDelete: true,
17575
+ locationLabel: "repo_local"
17576
+ });
17577
+ }
17578
+ signals.push("file_mutation");
17579
+ return classifyFileMutationWithPolicy({
17580
+ toolName,
17581
+ toolKind,
17582
+ filePath,
17583
+ resolvedPath,
17584
+ repoRoot,
17585
+ cwd,
17586
+ config,
17587
+ options,
17588
+ signals,
17589
+ isDelete: false,
17590
+ locationLabel: "repo_local"
17591
+ });
17592
+ }
17593
+ function classifyFilePathRead(params) {
17594
+ const {
17595
+ toolName,
17596
+ toolKind,
17597
+ filePath,
17598
+ repoRoot,
17599
+ cwd,
17600
+ config,
17601
+ options,
17602
+ sensitivePaths,
17603
+ protectedRoots
17604
+ } = params;
17605
+ const signals = ["effect.fs_read"];
17606
+ const resolvedPath = path51.isAbsolute(filePath) ? filePath : path51.resolve(cwd, filePath);
17607
+ if (protectedRoots.some((root) => pathWithinRoot(root, resolvedPath))) {
17608
+ signals.push("control_plane_path");
17609
+ return classifyFileReadWithPolicy({
17610
+ toolName,
17611
+ toolKind,
17612
+ filePath,
17613
+ resolvedPath,
17614
+ repoRoot,
17615
+ cwd,
17616
+ config,
17617
+ options,
17618
+ signals,
17619
+ locationLabel: "control_plane"
17620
+ });
17621
+ }
17622
+ const workspaceMatch = resolveWorkspaceRootMatch(
17623
+ repoRoot,
17624
+ options.trustedWorkspaceRoots,
17625
+ resolvedPath
17626
+ );
17627
+ if (workspaceMatch === null) {
17628
+ signals.push("outside_repo_path");
17629
+ return classifyFileReadWithPolicy({
17630
+ toolName,
17631
+ toolKind,
17632
+ filePath,
17633
+ resolvedPath,
17634
+ repoRoot,
17635
+ cwd,
17636
+ config,
17637
+ options,
17638
+ signals,
17639
+ locationLabel: "outside_repo"
17640
+ });
17641
+ }
17642
+ const relativePath = workspaceMatch.relativePath;
17643
+ if (matchesSensitivePath(relativePath, sensitivePaths)) {
17644
+ signals.push("sensitive_path");
17645
+ return classifyFileReadWithPolicy({
17646
+ toolName,
17647
+ toolKind,
17648
+ filePath,
17649
+ resolvedPath,
17650
+ repoRoot,
17651
+ cwd,
17652
+ config,
17653
+ options,
17654
+ signals,
17655
+ locationLabel: "sensitive_path"
17656
+ });
17657
+ }
17658
+ return classifyFileReadWithPolicy({
17659
+ toolName,
17660
+ toolKind,
17661
+ filePath,
17662
+ resolvedPath,
17663
+ repoRoot,
17664
+ cwd,
17665
+ config,
17666
+ options,
17667
+ signals,
17668
+ locationLabel: "repo_local"
17669
+ });
17670
+ }
17671
+ function indeterminateToolResult(toolName, payload, repoRoot, options) {
17672
+ const summary = canonicalStringify(scrubPayload(payload.tool_input ?? {}, options));
17673
+ const fingerprint = toolFingerprint(toolName, fingerprintPayload(payload, options), repoRoot);
17674
+ if (options.unknownLocalEffect === "deny") {
17675
+ return {
17676
+ verdict: "deny_pending_approval",
17677
+ reason: "indeterminate_tool_effect",
17678
+ summary,
17679
+ fingerprint,
17680
+ assessment: {
17681
+ reversibility: "irreversible",
17682
+ external: false,
17683
+ blastRadius: "unknown tool action",
17684
+ confidence: 0.5,
17685
+ signals: ["indeterminate_tool_effect"]
17686
+ }
17687
+ };
17688
+ }
17689
+ return {
17690
+ verdict: "allow_flagged",
17691
+ reason: "indeterminate_tool_effect",
17692
+ summary,
17693
+ fingerprint,
17694
+ assessment: {
17695
+ reversibility: "recoverable_with_cost",
17696
+ external: false,
17697
+ blastRadius: "tool scope",
17698
+ confidence: 0.5,
17699
+ signals: ["indeterminate_tool_effect"]
17700
+ }
17701
+ };
17702
+ }
17337
17703
  async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
17338
17704
  const toolName = String(payload.tool_name ?? "");
17339
17705
  const toolKind = normalizedToolName(toolName);
@@ -17380,9 +17746,10 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
17380
17746
  summary: command
17381
17747
  };
17382
17748
  }
17383
- if (FILE_WRITE_TOOL_NAMES.has(toolKind) || FILE_EDIT_TOOL_NAMES.has(toolKind) || FILE_DELETE_TOOL_NAMES.has(toolKind)) {
17384
- const filePath = extractFilePath(payload);
17385
- if (!filePath) {
17749
+ const effect = inferToolPayloadEffect(payload, toolName);
17750
+ if (effect === "file_mutation" || effect === "file_delete") {
17751
+ const filePath2 = extractFilePath(payload);
17752
+ if (!filePath2) {
17386
17753
  if (options.unknownLocalEffect === "deny") {
17387
17754
  return {
17388
17755
  verdict: "deny_pending_approval",
@@ -17412,138 +17779,20 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
17412
17779
  }
17413
17780
  };
17414
17781
  }
17415
- const signals = [];
17416
- const resolvedPath = path51.isAbsolute(filePath) ? filePath : path51.resolve(cwd, filePath);
17417
- const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
17418
- if (hitsProtectedRoot) {
17419
- signals.push("control_plane_path");
17420
- return classifyFileMutationWithPolicy({
17421
- toolName,
17422
- toolKind,
17423
- filePath,
17424
- resolvedPath,
17425
- repoRoot,
17426
- cwd,
17427
- config,
17428
- options,
17429
- signals,
17430
- isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
17431
- locationLabel: "control_plane"
17432
- });
17433
- }
17434
- const workspaceMatch = resolveWorkspaceRootMatch(
17435
- repoRoot,
17436
- options.trustedWorkspaceRoots,
17437
- resolvedPath
17438
- );
17439
- if (workspaceMatch === null) {
17440
- signals.push("outside_repo_path");
17441
- return classifyFileMutationWithPolicy({
17442
- toolName,
17443
- toolKind,
17444
- filePath,
17445
- resolvedPath,
17446
- repoRoot,
17447
- cwd,
17448
- config,
17449
- options,
17450
- signals,
17451
- isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
17452
- locationLabel: "outside_repo"
17453
- });
17454
- }
17455
- if (workspaceMatch.kind === "trusted") {
17456
- signals.push("trusted_workspace_root");
17457
- }
17458
- const trustedCwd = resolveClassifierTrustedCwd(cwd, options);
17459
- const workspacePrescan = mutationPrescanRequiresAsk({
17460
- targets: [filePath],
17461
- cwd,
17462
- repoRoot,
17463
- trustedCwd,
17464
- trustedWorkspaceRoots: options.trustedWorkspaceRoots,
17465
- sensitivePaths
17466
- });
17467
- if (workspacePrescan) {
17468
- return classifyFileMutationWithPolicy({
17469
- toolName,
17470
- toolKind,
17471
- filePath,
17472
- resolvedPath,
17473
- repoRoot,
17474
- cwd,
17475
- config,
17476
- options,
17477
- signals,
17478
- isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
17479
- locationLabel: "sensitive_path"
17480
- });
17481
- }
17482
- const relativePath = workspaceMatch.relativePath;
17483
- if (matchesSensitivePath(relativePath, sensitivePaths)) {
17484
- signals.push("sensitive_path");
17485
- return classifyFileMutationWithPolicy({
17486
- toolName,
17487
- toolKind,
17488
- filePath,
17489
- resolvedPath,
17490
- repoRoot,
17491
- cwd,
17492
- config,
17493
- options,
17494
- signals,
17495
- isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
17496
- locationLabel: "sensitive_path"
17497
- });
17498
- }
17499
- if (FILE_DELETE_TOOL_NAMES.has(toolKind)) {
17500
- if (isGitPath(resolvedPath, repoRoot)) {
17501
- signals.push("protected_artifact");
17502
- return classifyFileMutationWithPolicy({
17503
- toolName,
17504
- toolKind,
17505
- filePath,
17506
- resolvedPath,
17507
- repoRoot,
17508
- cwd,
17509
- config,
17510
- options,
17511
- signals,
17512
- isDelete: true,
17513
- locationLabel: "sensitive_path"
17514
- });
17515
- }
17516
- signals.push("file_delete");
17517
- return classifyFileMutationWithPolicy({
17518
- toolName,
17519
- toolKind,
17520
- filePath,
17521
- resolvedPath,
17522
- repoRoot,
17523
- cwd,
17524
- config,
17525
- options,
17526
- signals,
17527
- isDelete: true,
17528
- locationLabel: "repo_local"
17529
- });
17530
- }
17531
- signals.push("file_mutation");
17532
- return classifyFileMutationWithPolicy({
17782
+ return classifyFilePathMutation({
17533
17783
  toolName,
17534
17784
  toolKind,
17535
- filePath,
17536
- resolvedPath,
17785
+ filePath: filePath2,
17537
17786
  repoRoot,
17538
17787
  cwd,
17539
17788
  config,
17540
17789
  options,
17541
- signals,
17542
- isDelete: false,
17543
- locationLabel: "repo_local"
17790
+ sensitivePaths,
17791
+ protectedRoots,
17792
+ isDelete: effect === "file_delete"
17544
17793
  });
17545
17794
  }
17546
- if (APPLY_PATCH_TOOL_NAMES.has(toolKind)) {
17795
+ if (effect === "apply_patch") {
17547
17796
  const patch = extractPatch(payload);
17548
17797
  const targets = patch ? applyPatchTargets2(patch) : [];
17549
17798
  if (targets.length === 0) {
@@ -17581,7 +17830,7 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
17581
17830
  const result = await classifyToolUse(
17582
17831
  {
17583
17832
  tool_name: target.delete ? "Delete" : "Write",
17584
- tool_input: { path: target.path }
17833
+ tool_input: target.delete ? { path: target.path } : { path: target.path, contents: " " }
17585
17834
  },
17586
17835
  repoRoot,
17587
17836
  cwd,
@@ -17607,19 +17856,54 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
17607
17856
  }
17608
17857
  };
17609
17858
  }
17610
- return {
17611
- verdict: "allow",
17612
- reason: "unclassified_tool",
17613
- summary: canonicalStringify(scrubPayload(payload.tool_input ?? {}, options)),
17614
- fingerprint: toolFingerprint(toolName, fingerprintPayload(payload, options), repoRoot),
17615
- assessment: {
17616
- reversibility: "reversible",
17617
- external: false,
17618
- blastRadius: "tool scope",
17619
- confidence: 0.5,
17620
- signals: ["unclassified_tool"]
17859
+ if (effect === "file_read") {
17860
+ const filePath2 = extractFilePath(payload);
17861
+ if (!filePath2) {
17862
+ return indeterminateToolResult(toolName, payload, repoRoot, options);
17621
17863
  }
17622
- };
17864
+ return classifyFilePathRead({
17865
+ toolName,
17866
+ toolKind,
17867
+ filePath: filePath2,
17868
+ repoRoot,
17869
+ cwd,
17870
+ config,
17871
+ options,
17872
+ sensitivePaths,
17873
+ protectedRoots
17874
+ });
17875
+ }
17876
+ if (effect === "search_read") {
17877
+ return {
17878
+ verdict: "allow",
17879
+ reason: "effect.search_read",
17880
+ summary: canonicalStringify(scrubPayload(payload.tool_input ?? {}, options)),
17881
+ fingerprint: toolFingerprint(toolName, fingerprintPayload(payload, options), repoRoot),
17882
+ assessment: {
17883
+ reversibility: "reversible",
17884
+ external: false,
17885
+ blastRadius: "tool scope",
17886
+ confidence: 0.88,
17887
+ signals: ["effect.search_read"]
17888
+ }
17889
+ };
17890
+ }
17891
+ const filePath = extractFilePath(payload);
17892
+ if (filePath) {
17893
+ return classifyFilePathMutation({
17894
+ toolName,
17895
+ toolKind,
17896
+ filePath,
17897
+ repoRoot,
17898
+ cwd,
17899
+ config,
17900
+ options,
17901
+ sensitivePaths,
17902
+ protectedRoots,
17903
+ isDelete: toolKind === "delete"
17904
+ });
17905
+ }
17906
+ return indeterminateToolResult(toolName, payload, repoRoot, options);
17623
17907
  }
17624
17908
 
17625
17909
  // src/core/gate-engine.ts
@@ -17954,15 +18238,8 @@ function gateEnabledForAction(config, action) {
17954
18238
  if (action.kind === "subagent") {
17955
18239
  return config.gates.subagent;
17956
18240
  }
17957
- const toolName = action.toolName ?? String(action.payload?.tool_name ?? "");
17958
- if (toolName === "Shell") {
17959
- return config.gates.toolShell;
17960
- }
17961
- if (toolName === "Write" || toolName === "StrReplace" || toolName === "Delete") {
17962
- return config.gates.fileMutation;
17963
- }
17964
- if (toolName === "Task") {
17965
- return config.gates.subagent;
18241
+ if (action.kind === "tool") {
18242
+ return config.gates.fileMutation || config.gates.toolShell;
17966
18243
  }
17967
18244
  return true;
17968
18245
  }
@@ -17999,7 +18276,7 @@ function hashDecisionConfig(config) {
17999
18276
  init_fingerprint2();
18000
18277
 
18001
18278
  // src/version.ts
18002
- var PACKAGE_VERSION = "0.10.0";
18279
+ var PACKAGE_VERSION = "0.10.1";
18003
18280
 
18004
18281
  // src/runtime-provenance.ts
18005
18282
  function resolveRuntimeArtifactHash(artifactHash) {
@@ -21300,21 +21577,7 @@ async function evaluateGatedAction(ctx, deps, params) {
21300
21577
  ...authorization,
21301
21578
  egressProxyActive
21302
21579
  };
21303
- let predicted = await classifyGatedActionAsync(action, ctx.config, enrichedClassifierOptions);
21304
- if (action.kind === "tool" && predicted.reason === "unclassified_tool" && (ctx.config.policy.codexUnmappedTool ?? "deny") === "deny") {
21305
- predicted = {
21306
- ...predicted,
21307
- verdict: "deny_pending_approval",
21308
- reason: "unmapped_tool",
21309
- assessment: {
21310
- reversibility: "irreversible",
21311
- external: false,
21312
- blastRadius: "unknown tool action",
21313
- confidence: 0.5,
21314
- signals: ["unmapped_tool"]
21315
- }
21316
- };
21317
- }
21580
+ const predicted = await classifyGatedActionAsync(action, ctx.config, enrichedClassifierOptions);
21318
21581
  if (action.kind === "shell" && action.command && isContainedUnknownExecutionEligible(ctx.config, action, predicted)) {
21319
21582
  const mediated = await mediateContainedUnknownExecution({
21320
21583
  ctx,