@gotgenes/pi-permission-system 9.0.0 → 9.1.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.
@@ -3,7 +3,8 @@ import type { Rule } from "#src/rule";
3
3
  import { SessionApproval } from "#src/session-approval";
4
4
  import { deriveApprovalPattern } from "#src/session-rules";
5
5
  import type { PermissionCheckResult } from "#src/types";
6
- import { extractTokensForPathRules } from "./bash-path-extractor";
6
+ import type { BashProgram } from "./bash-program";
7
+ import { pickMostRestrictive } from "./candidate-check";
7
8
  import type { GateResult } from "./descriptor";
8
9
  import { formatPathAskPrompt } from "./path";
9
10
  import type { ToolCallContext } from "./types";
@@ -19,8 +20,8 @@ type CheckPermissionFn = (
19
20
  /**
20
21
  * Build a pure descriptor for the cross-cutting path permission gate (bash).
21
22
  *
22
- * Extracts path-candidate tokens from a bash command using tree-sitter with
23
- * the broader filter (accepts dot-files, relative paths). Evaluates each
23
+ * Reads path-candidate tokens from the injected `BashProgram` (the broader
24
+ * `path`-rule filter, accepting dot-files and relative paths). Evaluates each
24
25
  * token against the `path` permission surface and returns the most
25
26
  * restrictive result.
26
27
  *
@@ -29,24 +30,28 @@ type CheckPermissionFn = (
29
30
  * Returns a `GateBypass` when all tokens are session-covered.
30
31
  * Returns a `GateDescriptor` for the most restrictive token needing a check.
31
32
  */
32
- export async function describeBashPathGate(
33
+ export function describeBashPathGate(
33
34
  tcc: ToolCallContext,
35
+ bashProgram: BashProgram | null,
34
36
  checkPermission: CheckPermissionFn,
35
37
  getSessionRuleset: () => Rule[],
36
- ): Promise<GateResult> {
38
+ ): GateResult {
37
39
  if (tcc.toolName !== "bash") return null;
38
40
 
39
41
  const command = getNonEmptyString(toRecord(tcc.input).command);
40
42
  if (!command) return null;
41
43
 
42
- const tokens = await extractTokensForPathRules(command);
44
+ if (!bashProgram) return null;
45
+
46
+ const tokens = bashProgram.pathTokens();
43
47
  if (tokens.length === 0) return null;
44
48
 
45
49
  // Check each token against path rules with session rules appended.
46
50
  const sessionRules = getSessionRuleset();
47
51
 
48
- let worstCheck: PermissionCheckResult | null = null;
49
- let worstToken: string | null = null;
52
+ // Tokens whose resolved state needs a check (deny/ask), paired with the
53
+ // token that produced them so the descriptor can derive its pattern.
54
+ const uncovered: Array<{ token: string; check: PermissionCheckResult }> = [];
50
55
  let allSessionCovered = true;
51
56
 
52
57
  for (const token of tokens) {
@@ -70,13 +75,11 @@ export async function describeBashPathGate(
70
75
  }
71
76
 
72
77
  if (check.state === "deny") {
73
- worstCheck = check;
74
- worstToken = token;
78
+ uncovered.push({ token, check });
75
79
  break; // Short-circuit on deny.
76
80
  }
77
- if (check.state === "ask" && worstCheck?.state !== "ask") {
78
- worstCheck = check;
79
- worstToken = token;
81
+ if (check.state === "ask") {
82
+ uncovered.push({ token, check });
80
83
  }
81
84
  }
82
85
 
@@ -99,6 +102,12 @@ export async function describeBashPathGate(
99
102
  };
100
103
  }
101
104
 
105
+ // Pick the most restrictive (deny > ask > allow, first-wins) uncovered token.
106
+ const worstCheck = pickMostRestrictive(uncovered.map(({ check }) => check));
107
+ const worstToken = worstCheck
108
+ ? (uncovered.find(({ check }) => check === worstCheck)?.token ?? null)
109
+ : null;
110
+
102
111
  // All tokens evaluate to allow — no restriction.
103
112
  if (!worstCheck || !worstToken) return null;
104
113