@gotgenes/pi-permission-system 5.5.1 → 5.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/handlers/gates/bash-external-directory.ts +70 -65
- package/src/handlers/gates/descriptor.ts +115 -0
- package/src/handlers/gates/external-directory.ts +63 -122
- package/src/handlers/gates/index.ts +12 -4
- package/src/handlers/gates/runner.ts +144 -0
- package/src/handlers/gates/skill-read.ts +37 -54
- package/src/handlers/gates/tool.ts +35 -97
- package/src/handlers/gates/types.ts +0 -77
- package/src/handlers/tool-call.ts +89 -58
- package/tests/handlers/gates/bash-external-directory.test.ts +128 -126
- package/tests/handlers/gates/external-directory.test.ts +117 -188
- package/tests/handlers/gates/runner.test.ts +361 -0
- package/tests/handlers/gates/skill-read.test.ts +87 -123
- package/tests/handlers/gates/tool.test.ts +119 -112
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.6.0](https://github.com/gotgenes/pi-permission-system/compare/v5.5.1...v5.6.0) (2026-05-07)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* implement runGateCheck gate runner ([#118](https://github.com/gotgenes/pi-permission-system/issues/118)) ([46da4b6](https://github.com/gotgenes/pi-permission-system/commit/46da4b616cddef22d9e1d198a3aac9c640d62bac))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* plan gate runner extraction ([#118](https://github.com/gotgenes/pi-permission-system/issues/118)) ([8c0eb18](https://github.com/gotgenes/pi-permission-system/commit/8c0eb1881dabc19dba65f72ba5c30ae02e4070a0))
|
|
19
|
+
* **retro:** add retro notes for issue [#111](https://github.com/gotgenes/pi-permission-system/issues/111) ([e327323](https://github.com/gotgenes/pi-permission-system/commit/e327323187822e779454eeafd7372c6256f869ba))
|
|
20
|
+
* update target architecture for gate runner ([#118](https://github.com/gotgenes/pi-permission-system/issues/118)) ([40e1b1b](https://github.com/gotgenes/pi-permission-system/commit/40e1b1b016730e9be3da18fcb831001a2f498081))
|
|
21
|
+
|
|
8
22
|
## [5.5.1](https://github.com/gotgenes/pi-permission-system/compare/v5.5.0...v5.5.1) (2026-05-07)
|
|
9
23
|
|
|
10
24
|
|
package/package.json
CHANGED
|
@@ -5,26 +5,34 @@ import {
|
|
|
5
5
|
formatBashExternalDirectoryDenyReason,
|
|
6
6
|
formatExternalDirectoryHardStopHint,
|
|
7
7
|
} from "../../external-directory";
|
|
8
|
-
import type {
|
|
9
|
-
import { applyPermissionGate } from "../../permission-gate";
|
|
8
|
+
import type { Rule } from "../../rule";
|
|
10
9
|
import { deriveApprovalPattern } from "../../session-rules";
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
import type { PermissionCheckResult } from "../../types";
|
|
11
|
+
import type { GateResult } from "./descriptor";
|
|
12
|
+
import type { ToolCallContext } from "./types";
|
|
13
|
+
|
|
14
|
+
/** Function type for checkPermission used by the descriptor factory. */
|
|
15
|
+
type CheckPermissionFn = (
|
|
16
|
+
surface: string,
|
|
17
|
+
input: unknown,
|
|
18
|
+
agentName?: string,
|
|
19
|
+
sessionRules?: Rule[],
|
|
20
|
+
) => PermissionCheckResult;
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
23
|
+
* Build a pure descriptor for the bash external-directory permission gate.
|
|
19
24
|
*
|
|
20
25
|
* Extracts paths from a bash command and checks whether any reference
|
|
21
26
|
* directories outside the working directory. Returns `null` when the gate
|
|
22
27
|
* does not apply (tool is not bash, no CWD, or no external paths found).
|
|
28
|
+
* Returns a `GateBypass` when all paths are session-covered.
|
|
29
|
+
* Returns a `GateDescriptor` with multi-pattern sessionApproval for uncovered paths.
|
|
23
30
|
*/
|
|
24
|
-
export async function
|
|
31
|
+
export async function describeBashExternalDirectoryGate(
|
|
25
32
|
tcc: ToolCallContext,
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
checkPermission: CheckPermissionFn,
|
|
34
|
+
getSessionRuleset: () => Rule[],
|
|
35
|
+
): Promise<GateResult> {
|
|
28
36
|
if (tcc.toolName !== "bash" || !tcc.cwd) return null;
|
|
29
37
|
|
|
30
38
|
const command = getNonEmptyString(toRecord(tcc.input).command);
|
|
@@ -36,10 +44,10 @@ export async function evaluateBashExternalDirectoryGate(
|
|
|
36
44
|
);
|
|
37
45
|
if (externalPaths.length === 0) return null;
|
|
38
46
|
|
|
39
|
-
const bashSessionRules =
|
|
47
|
+
const bashSessionRules = getSessionRuleset();
|
|
40
48
|
const uncoveredPaths = externalPaths.filter(
|
|
41
49
|
(p) =>
|
|
42
|
-
|
|
50
|
+
checkPermission(
|
|
43
51
|
"external_directory",
|
|
44
52
|
{ path: p },
|
|
45
53
|
tcc.agentName ?? undefined,
|
|
@@ -48,58 +56,42 @@ export async function evaluateBashExternalDirectoryGate(
|
|
|
48
56
|
);
|
|
49
57
|
|
|
50
58
|
if (uncoveredPaths.length === 0) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
return {
|
|
60
|
+
action: "allow",
|
|
61
|
+
log: {
|
|
62
|
+
event: "permission_request.session_approved",
|
|
63
|
+
details: {
|
|
64
|
+
source: "tool_call",
|
|
65
|
+
toolCallId: tcc.toolCallId,
|
|
66
|
+
toolName: tcc.toolName,
|
|
67
|
+
agentName: tcc.agentName,
|
|
68
|
+
command,
|
|
69
|
+
externalPaths,
|
|
70
|
+
resolution: "session_approved",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
// Get the config-level policy (no path → no session check).
|
|
64
|
-
const extCheck =
|
|
77
|
+
const extCheck = checkPermission(
|
|
65
78
|
"external_directory",
|
|
66
79
|
{},
|
|
67
80
|
tcc.agentName ?? undefined,
|
|
68
81
|
);
|
|
69
82
|
|
|
70
|
-
let bashExtDecision: PermissionPromptDecision | null = null;
|
|
71
83
|
const bashExtMessage = formatBashExternalDirectoryAskPrompt(
|
|
72
84
|
command,
|
|
73
85
|
uncoveredPaths,
|
|
74
86
|
tcc.cwd,
|
|
75
87
|
tcc.agentName ?? undefined,
|
|
76
88
|
);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
source: "tool_call",
|
|
84
|
-
agentName: tcc.agentName,
|
|
85
|
-
message: bashExtMessage,
|
|
86
|
-
toolCallId: tcc.toolCallId,
|
|
87
|
-
toolName: tcc.toolName,
|
|
88
|
-
command,
|
|
89
|
-
});
|
|
90
|
-
bashExtDecision = decision;
|
|
91
|
-
return decision;
|
|
92
|
-
},
|
|
93
|
-
writeLog: deps.writeReviewLog,
|
|
94
|
-
logContext: {
|
|
95
|
-
source: "tool_call",
|
|
96
|
-
toolCallId: tcc.toolCallId,
|
|
97
|
-
toolName: tcc.toolName,
|
|
98
|
-
agentName: tcc.agentName,
|
|
99
|
-
command,
|
|
100
|
-
externalPaths: uncoveredPaths,
|
|
101
|
-
message: bashExtMessage,
|
|
102
|
-
},
|
|
89
|
+
|
|
90
|
+
const patterns = uncoveredPaths.map((p) => deriveApprovalPattern(p));
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
surface: "external_directory",
|
|
94
|
+
input: {},
|
|
103
95
|
messages: {
|
|
104
96
|
denyReason: formatBashExternalDirectoryDenyReason(
|
|
105
97
|
command,
|
|
@@ -115,18 +107,31 @@ export async function evaluateBashExternalDirectoryGate(
|
|
|
115
107
|
return `User denied external directory access for bash command '${command}'.${reasonSuffix} ${formatExternalDirectoryHardStopHint()}`;
|
|
116
108
|
},
|
|
117
109
|
},
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
110
|
+
sessionApproval: {
|
|
111
|
+
surface: "external_directory",
|
|
112
|
+
patterns,
|
|
113
|
+
},
|
|
114
|
+
promptDetails: {
|
|
115
|
+
source: "tool_call",
|
|
116
|
+
agentName: tcc.agentName,
|
|
117
|
+
message: bashExtMessage,
|
|
118
|
+
toolCallId: tcc.toolCallId,
|
|
119
|
+
toolName: tcc.toolName,
|
|
120
|
+
command,
|
|
121
|
+
},
|
|
122
|
+
logContext: {
|
|
123
|
+
source: "tool_call",
|
|
124
|
+
toolCallId: tcc.toolCallId,
|
|
125
|
+
toolName: tcc.toolName,
|
|
126
|
+
agentName: tcc.agentName,
|
|
127
|
+
command,
|
|
128
|
+
externalPaths: uncoveredPaths,
|
|
129
|
+
message: bashExtMessage,
|
|
130
|
+
},
|
|
131
|
+
decision: {
|
|
132
|
+
surface: "external_directory",
|
|
133
|
+
value: command,
|
|
134
|
+
},
|
|
135
|
+
preCheck: extCheck,
|
|
136
|
+
};
|
|
132
137
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { PermissionPromptDecision } from "../../permission-dialog";
|
|
2
|
+
import type {
|
|
3
|
+
PermissionDecisionEvent,
|
|
4
|
+
PermissionDecisionResolution,
|
|
5
|
+
} from "../../permission-events";
|
|
6
|
+
import type { Rule } from "../../rule";
|
|
7
|
+
import type { PermissionCheckResult, PermissionState } from "../../types";
|
|
8
|
+
import type { PromptPermissionDetails } from "../types";
|
|
9
|
+
|
|
10
|
+
// ── Descriptor types ───────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pure output of a gate function — describes what to check and how to present it.
|
|
14
|
+
*
|
|
15
|
+
* The gate runner (`runGateCheck`) uses this descriptor to execute the
|
|
16
|
+
* mechanical check→log→emit→approve cycle without the gate needing to know
|
|
17
|
+
* about logging, event emission, or session-rule recording.
|
|
18
|
+
*/
|
|
19
|
+
export interface GateDescriptor {
|
|
20
|
+
/** Permission surface to check (e.g. "bash", "external_directory", "skill"). */
|
|
21
|
+
surface: string;
|
|
22
|
+
/** Input passed to checkPermission. */
|
|
23
|
+
input: unknown;
|
|
24
|
+
/** Message strings/factories for each outcome. */
|
|
25
|
+
messages: {
|
|
26
|
+
denyReason: string;
|
|
27
|
+
unavailableReason: string;
|
|
28
|
+
userDeniedReason: (decision: PermissionPromptDecision) => string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Session-approval suggestion for "for this session" option.
|
|
32
|
+
* Single pattern or multiple patterns (bash external-directory gate).
|
|
33
|
+
*/
|
|
34
|
+
sessionApproval?:
|
|
35
|
+
| { surface: string; pattern: string }
|
|
36
|
+
| { surface: string; patterns: string[] };
|
|
37
|
+
/** Details passed to the interactive permission prompt (requestId is added by the runner). */
|
|
38
|
+
promptDetails: Omit<PromptPermissionDetails, "requestId">;
|
|
39
|
+
/** Extra context fields written to the review log alongside gate outcomes. */
|
|
40
|
+
logContext: Record<string, unknown>;
|
|
41
|
+
/** Surface and value for the decision event (may differ from the check surface). */
|
|
42
|
+
decision: {
|
|
43
|
+
surface: string;
|
|
44
|
+
value: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* When set, the gate has already resolved the permission state
|
|
48
|
+
* (e.g. from a skill entry match). The runner uses this directly
|
|
49
|
+
* instead of calling checkPermission.
|
|
50
|
+
*/
|
|
51
|
+
preResolved?: {
|
|
52
|
+
state: PermissionState;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* When set, the runner uses this pre-computed check result directly
|
|
56
|
+
* instead of calling checkPermission. Used when the orchestrator has
|
|
57
|
+
* already performed the check (e.g. to build messages from the result).
|
|
58
|
+
*/
|
|
59
|
+
preCheck?: PermissionCheckResult;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Early allow result — gate has determined the action without needing the runner.
|
|
64
|
+
*
|
|
65
|
+
* Used for cases like Pi infrastructure read bypass where the gate short-circuits
|
|
66
|
+
* with a deterministic allow before reaching the permission check.
|
|
67
|
+
*/
|
|
68
|
+
export interface GateBypass {
|
|
69
|
+
action: "allow";
|
|
70
|
+
/** Optional review log entry to emit. */
|
|
71
|
+
log?: { event: string; details: Record<string, unknown> };
|
|
72
|
+
/** Optional decision event to emit. */
|
|
73
|
+
decision?: PermissionDecisionEvent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Union of possible gate function return values. */
|
|
77
|
+
export type GateResult = GateDescriptor | GateBypass | null;
|
|
78
|
+
|
|
79
|
+
// ── Runner dependency interface ────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Infrastructure dependencies for the gate runner.
|
|
83
|
+
*
|
|
84
|
+
* Built once in the orchestrator and reused for all gates.
|
|
85
|
+
* Handles all side effects: permission checks, logging, event emission,
|
|
86
|
+
* session-rule recording.
|
|
87
|
+
*/
|
|
88
|
+
export interface GateRunnerDeps {
|
|
89
|
+
checkPermission(
|
|
90
|
+
surface: string,
|
|
91
|
+
input: unknown,
|
|
92
|
+
agentName?: string,
|
|
93
|
+
sessionRules?: Rule[],
|
|
94
|
+
): PermissionCheckResult;
|
|
95
|
+
getSessionRuleset(): Rule[];
|
|
96
|
+
approveSessionRule(surface: string, pattern: string): void;
|
|
97
|
+
writeReviewLog(event: string, details: Record<string, unknown>): void;
|
|
98
|
+
emitDecision(event: PermissionDecisionEvent): void;
|
|
99
|
+
canConfirm(): boolean;
|
|
100
|
+
promptPermission(
|
|
101
|
+
details: PromptPermissionDetails,
|
|
102
|
+
): Promise<PermissionPromptDecision>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Type guard helpers ─────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/** Check whether a GateResult is a GateBypass (early allow). */
|
|
108
|
+
export function isGateBypass(result: GateResult): result is GateBypass {
|
|
109
|
+
return result !== null && "action" in result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Check whether a GateResult is a GateDescriptor (needs runner). */
|
|
113
|
+
export function isGateDescriptor(result: GateResult): result is GateDescriptor {
|
|
114
|
+
return result !== null && !("action" in result);
|
|
115
|
+
}
|
|
@@ -7,26 +7,22 @@ import {
|
|
|
7
7
|
isPiInfrastructureRead,
|
|
8
8
|
normalizePathForComparison,
|
|
9
9
|
} from "../../external-directory";
|
|
10
|
-
import type { PermissionPromptDecision } from "../../permission-dialog";
|
|
11
|
-
import { applyPermissionGate } from "../../permission-gate";
|
|
12
10
|
import { deriveApprovalPattern } from "../../session-rules";
|
|
13
|
-
import {
|
|
14
|
-
import type {
|
|
15
|
-
ExternalDirectoryGateDeps,
|
|
16
|
-
GateOutcome,
|
|
17
|
-
ToolCallContext,
|
|
18
|
-
} from "./types";
|
|
11
|
+
import type { GateResult } from "./descriptor";
|
|
12
|
+
import type { ToolCallContext } from "./types";
|
|
19
13
|
|
|
20
14
|
/**
|
|
21
|
-
*
|
|
15
|
+
* Build a pure descriptor for the external-directory permission gate.
|
|
22
16
|
*
|
|
23
17
|
* Returns `null` when the gate does not apply (no CWD, tool is not
|
|
24
18
|
* path-bearing, or path is inside the working directory).
|
|
19
|
+
* Returns a `GateBypass` for Pi infrastructure reads.
|
|
20
|
+
* Returns a `GateDescriptor` for external paths needing a permission check.
|
|
25
21
|
*/
|
|
26
|
-
export
|
|
22
|
+
export function describeExternalDirectoryGate(
|
|
27
23
|
tcc: ToolCallContext,
|
|
28
|
-
|
|
29
|
-
):
|
|
24
|
+
infraDirs: string[],
|
|
25
|
+
): GateResult {
|
|
30
26
|
if (!tcc.cwd) return null;
|
|
31
27
|
|
|
32
28
|
const externalDirectoryPath = getPathBearingToolPath(tcc.toolName, tcc.input);
|
|
@@ -42,99 +38,46 @@ export async function evaluateExternalDirectoryGate(
|
|
|
42
38
|
);
|
|
43
39
|
|
|
44
40
|
// ── Pi infrastructure read bypass ──────────────────────────────────────
|
|
45
|
-
const allInfraDirs = deps.getInfrastructureDirs();
|
|
46
41
|
if (
|
|
47
|
-
isPiInfrastructureRead(
|
|
48
|
-
tcc.toolName,
|
|
49
|
-
normalizedExtPath,
|
|
50
|
-
allInfraDirs,
|
|
51
|
-
tcc.cwd,
|
|
52
|
-
)
|
|
42
|
+
isPiInfrastructureRead(tcc.toolName, normalizedExtPath, infraDirs, tcc.cwd)
|
|
53
43
|
) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
44
|
+
return {
|
|
45
|
+
action: "allow",
|
|
46
|
+
log: {
|
|
47
|
+
event: "permission_request.infrastructure_auto_allowed",
|
|
48
|
+
details: {
|
|
49
|
+
source: "tool_call",
|
|
50
|
+
toolCallId: tcc.toolCallId,
|
|
51
|
+
toolName: tcc.toolName,
|
|
52
|
+
agentName: tcc.agentName,
|
|
53
|
+
path: externalDirectoryPath,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
decision: {
|
|
57
|
+
surface: tcc.toolName,
|
|
58
|
+
value: externalDirectoryPath,
|
|
59
|
+
result: "allow",
|
|
60
|
+
resolution: "infrastructure_auto_allowed",
|
|
61
|
+
origin: null,
|
|
62
|
+
agentName: tcc.agentName ?? null,
|
|
63
|
+
matchedPattern: null,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
71
66
|
}
|
|
72
67
|
|
|
73
|
-
// ──
|
|
74
|
-
const extCheck = deps.checkPermission(
|
|
75
|
-
"external_directory",
|
|
76
|
-
{ path: normalizedExtPath },
|
|
77
|
-
tcc.agentName ?? undefined,
|
|
78
|
-
deps.getSessionRuleset(),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Session-rule hit
|
|
82
|
-
if (extCheck.source === "session") {
|
|
83
|
-
deps.writeReviewLog("permission_request.session_approved", {
|
|
84
|
-
source: "tool_call",
|
|
85
|
-
toolCallId: tcc.toolCallId,
|
|
86
|
-
toolName: tcc.toolName,
|
|
87
|
-
agentName: tcc.agentName,
|
|
88
|
-
path: externalDirectoryPath,
|
|
89
|
-
resolution: "session_approved",
|
|
90
|
-
sessionApprovalPattern: extCheck.matchedPattern,
|
|
91
|
-
});
|
|
92
|
-
deps.emitDecision({
|
|
93
|
-
surface: "external_directory",
|
|
94
|
-
value: externalDirectoryPath,
|
|
95
|
-
result: "allow",
|
|
96
|
-
resolution: "session_approved",
|
|
97
|
-
origin: extCheck.origin ?? null,
|
|
98
|
-
agentName: tcc.agentName ?? null,
|
|
99
|
-
matchedPattern: extCheck.matchedPattern ?? null,
|
|
100
|
-
});
|
|
101
|
-
return { action: "allow" };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ── Interactive gate ───────────────────────────────────────────────────
|
|
105
|
-
let extDirDecision: PermissionPromptDecision | null = null;
|
|
68
|
+
// ── Build descriptor for permission check ───────────────────────────────
|
|
106
69
|
const extDirMessage = formatExternalDirectoryAskPrompt(
|
|
107
70
|
tcc.toolName,
|
|
108
71
|
externalDirectoryPath,
|
|
109
72
|
tcc.cwd,
|
|
110
73
|
tcc.agentName ?? undefined,
|
|
111
74
|
);
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
requestId: tcc.toolCallId,
|
|
119
|
-
source: "tool_call",
|
|
120
|
-
agentName: tcc.agentName,
|
|
121
|
-
message: extDirMessage,
|
|
122
|
-
toolCallId: tcc.toolCallId,
|
|
123
|
-
toolName: tcc.toolName,
|
|
124
|
-
path: externalDirectoryPath,
|
|
125
|
-
});
|
|
126
|
-
extDirDecision = decision;
|
|
127
|
-
return decision;
|
|
128
|
-
},
|
|
129
|
-
writeLog: deps.writeReviewLog,
|
|
130
|
-
logContext: {
|
|
131
|
-
source: "tool_call",
|
|
132
|
-
toolCallId: tcc.toolCallId,
|
|
133
|
-
toolName: tcc.toolName,
|
|
134
|
-
agentName: tcc.agentName,
|
|
135
|
-
path: externalDirectoryPath,
|
|
136
|
-
message: extDirMessage,
|
|
137
|
-
},
|
|
75
|
+
|
|
76
|
+
const pattern = deriveApprovalPattern(normalizedExtPath);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
surface: "external_directory",
|
|
80
|
+
input: { path: normalizedExtPath },
|
|
138
81
|
messages: {
|
|
139
82
|
denyReason: formatExternalDirectoryDenyReason(
|
|
140
83
|
tcc.toolName,
|
|
@@ -150,31 +93,29 @@ export async function evaluateExternalDirectoryGate(
|
|
|
150
93
|
decision.denialReason,
|
|
151
94
|
),
|
|
152
95
|
},
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return { action: "allow" };
|
|
96
|
+
sessionApproval: {
|
|
97
|
+
surface: "external_directory",
|
|
98
|
+
pattern,
|
|
99
|
+
},
|
|
100
|
+
promptDetails: {
|
|
101
|
+
source: "tool_call",
|
|
102
|
+
agentName: tcc.agentName,
|
|
103
|
+
message: extDirMessage,
|
|
104
|
+
toolCallId: tcc.toolCallId,
|
|
105
|
+
toolName: tcc.toolName,
|
|
106
|
+
path: externalDirectoryPath,
|
|
107
|
+
},
|
|
108
|
+
logContext: {
|
|
109
|
+
source: "tool_call",
|
|
110
|
+
toolCallId: tcc.toolCallId,
|
|
111
|
+
toolName: tcc.toolName,
|
|
112
|
+
agentName: tcc.agentName,
|
|
113
|
+
path: externalDirectoryPath,
|
|
114
|
+
message: extDirMessage,
|
|
115
|
+
},
|
|
116
|
+
decision: {
|
|
117
|
+
surface: "external_directory",
|
|
118
|
+
value: externalDirectoryPath,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
180
121
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { describeBashExternalDirectoryGate } from "./bash-external-directory";
|
|
2
|
+
export type {
|
|
3
|
+
GateBypass,
|
|
4
|
+
GateDescriptor,
|
|
5
|
+
GateResult,
|
|
6
|
+
GateRunnerDeps,
|
|
7
|
+
} from "./descriptor";
|
|
8
|
+
export { isGateBypass, isGateDescriptor } from "./descriptor";
|
|
9
|
+
export { describeExternalDirectoryGate } from "./external-directory";
|
|
3
10
|
export { deriveDecisionValue, deriveResolution } from "./helpers";
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
11
|
+
export { runGateCheck } from "./runner";
|
|
12
|
+
export { describeSkillReadGate } from "./skill-read";
|
|
13
|
+
export { describeToolGate } from "./tool";
|
|
6
14
|
export type { GateOutcome, ToolCallContext } from "./types";
|