@gotgenes/pi-permission-system 16.1.0 → 16.2.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.
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/access-intent/access-intent.ts +57 -0
- package/src/access-intent/bash/command-enumeration.ts +79 -2
- package/src/access-intent/bash/parser.ts +2 -0
- package/src/access-intent/bash/program.ts +3 -0
- package/src/builtin-tool-input-formatters.ts +1 -1
- package/src/config-loader.ts +7 -7
- package/src/forwarded-permissions/permission-forwarder.ts +1 -1
- package/src/handlers/gates/bash-command.ts +32 -3
- package/src/handlers/gates/bash-external-directory.ts +1 -1
- package/src/handlers/gates/bash-path.ts +7 -5
- package/src/handlers/gates/external-directory-policy.ts +9 -8
- package/src/handlers/gates/path.ts +6 -5
- package/src/handlers/gates/runner.ts +6 -5
- package/src/handlers/gates/skill-read.ts +1 -1
- package/src/handlers/gates/tool-call-gate-pipeline.ts +7 -6
- package/src/handlers/permission-gate-handler.ts +1 -2
- package/src/handlers/tool-call-boundary.ts +1 -2
- package/src/input-normalizer.ts +1 -1
- package/src/mcp-targets.ts +1 -1
- package/src/normalize.ts +1 -1
- package/src/path-utils.ts +1 -1
- package/src/permission-event-rpc.ts +4 -6
- package/src/permission-manager.ts +40 -56
- package/src/permission-prompts.ts +1 -1
- package/src/permission-resolver.ts +45 -57
- package/src/permissions-service.ts +2 -4
- package/src/policy-loader.ts +2 -2
- package/src/skill-prompt-sanitizer.ts +2 -2
- package/src/tool-input-prompt-formatters.ts +1 -1
- package/src/tool-preview-formatter.ts +1 -1
- package/src/tool-registry.ts +1 -1
- package/src/{common.ts → value-guards.ts} +0 -66
- package/src/yaml-frontmatter.ts +65 -0
- package/test/access-intent/bash/node-text.test.ts +1 -0
- package/test/access-intent/bash/program.test.ts +67 -0
- package/test/handlers/before-agent-start.test.ts +6 -9
- package/test/handlers/external-directory-integration.test.ts +40 -153
- package/test/handlers/external-directory-session-dedup.test.ts +38 -286
- package/test/handlers/gates/bash-command-metamorphic.test.ts +5 -3
- package/test/handlers/gates/bash-command.test.ts +89 -23
- package/test/handlers/gates/bash-external-directory.test.ts +37 -33
- package/test/handlers/gates/bash-path.test.ts +13 -9
- package/test/handlers/gates/external-directory-policy.test.ts +34 -22
- package/test/handlers/gates/external-directory.test.ts +11 -5
- package/test/handlers/gates/path.test.ts +30 -25
- package/test/handlers/gates/runner.test.ts +6 -1
- package/test/handlers/gates/tool-call-gate-pipeline.test.ts +2 -2
- package/test/handlers/input.test.ts +1 -1
- package/test/helpers/external-directory-fixtures.ts +269 -0
- package/test/helpers/gate-fixtures.ts +12 -24
- package/test/helpers/handler-fixtures.ts +21 -15
- package/test/helpers/session-fixtures.ts +3 -18
- package/test/permission-event-rpc.test.ts +24 -20
- package/test/permission-manager-unified.test.ts +445 -209
- package/test/permission-resolver.test.ts +112 -89
- package/test/permissions-service.test.ts +10 -7
- package/test/skill-prompt-sanitizer.test.ts +30 -7
- package/test/{common.test.ts → value-guards.test.ts} +2 -96
- package/test/yaml-frontmatter.test.ts +91 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { AccessIntent } from "#src/access-intent/access-intent";
|
|
2
3
|
import { BashProgram } from "#src/access-intent/bash/program";
|
|
3
|
-
import { getNonEmptyString, toRecord } from "#src/common";
|
|
4
4
|
import { describeBashExternalDirectoryGate } from "#src/handlers/gates/bash-external-directory";
|
|
5
5
|
import type {
|
|
6
6
|
GateBypass,
|
|
@@ -11,6 +11,7 @@ import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
|
11
11
|
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
12
12
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
13
13
|
import type { PermissionCheckResult } from "#src/types";
|
|
14
|
+
import { getNonEmptyString, toRecord } from "#src/value-guards";
|
|
14
15
|
|
|
15
16
|
import { makeResolver } from "#test/helpers/gate-fixtures";
|
|
16
17
|
|
|
@@ -40,6 +41,13 @@ function makeCheckResult(
|
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/** Extract the policy match values a resolve(intent) call carries. */
|
|
45
|
+
function intentValues(intent: AccessIntent): readonly string[] {
|
|
46
|
+
if (intent.kind === "access-path") return intent.path.matchValues();
|
|
47
|
+
if (intent.kind === "path-values") return intent.values;
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
/**
|
|
44
52
|
* Mirror the handler's parse-once derivation: parse the bash command into a
|
|
45
53
|
* shared `BashProgram` and inject it, exactly as `permission-gate-handler.ts`
|
|
@@ -76,18 +84,19 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
76
84
|
expect(result).toBeNull();
|
|
77
85
|
});
|
|
78
86
|
|
|
79
|
-
it("resolves each external path on the external_directory surface via
|
|
87
|
+
it("resolves each external path on the external_directory surface via an access-path intent (#418)", async () => {
|
|
80
88
|
const resolver = makeResolver(makeCheckResult("ask"));
|
|
81
89
|
await describeGate(
|
|
82
90
|
makeTcc({ input: { command: "cat /outside/a.ts" } }),
|
|
83
91
|
resolver,
|
|
84
92
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"external_directory",
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
const intent = resolver.resolve.mock.calls[0][0];
|
|
94
|
+
expect(intent).toMatchObject({
|
|
95
|
+
kind: "access-path",
|
|
96
|
+
surface: "external_directory",
|
|
97
|
+
agentName: undefined,
|
|
98
|
+
});
|
|
99
|
+
expect(intentValues(intent)).toEqual(["/outside/a.ts"]);
|
|
91
100
|
});
|
|
92
101
|
|
|
93
102
|
it("returns GateBypass when all external paths are session-covered", async () => {
|
|
@@ -122,11 +131,10 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
122
131
|
// not just session-level allow. This was the bug: source !== "session"
|
|
123
132
|
// kept config-allowed paths in the uncovered set.
|
|
124
133
|
const resolver = makeResolver();
|
|
125
|
-
resolver.
|
|
126
|
-
(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
: makeCheckResult("ask"),
|
|
134
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
135
|
+
intentValues(intent).length > 0
|
|
136
|
+
? makeCheckResult("allow", { source: "special" })
|
|
137
|
+
: makeCheckResult("ask"),
|
|
130
138
|
);
|
|
131
139
|
const result = await describeGate(makeTcc(), resolver);
|
|
132
140
|
expect(result).not.toBeNull();
|
|
@@ -138,11 +146,10 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
138
146
|
// silently downgrading a config-level deny to ask. After the fix, the
|
|
139
147
|
// descriptor's preCheck is derived from the actual path check result.
|
|
140
148
|
const resolver = makeResolver();
|
|
141
|
-
resolver.
|
|
142
|
-
(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
: makeCheckResult("ask"),
|
|
149
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
150
|
+
intentValues(intent).length > 0
|
|
151
|
+
? makeCheckResult("deny", { source: "special" })
|
|
152
|
+
: makeCheckResult("ask"),
|
|
146
153
|
);
|
|
147
154
|
const result = await describeGate(makeTcc(), resolver);
|
|
148
155
|
expect(isGateDescriptor(result)).toBe(true);
|
|
@@ -199,11 +206,10 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
199
206
|
it("config-allowed path is excluded; remaining ask path produces a descriptor", async () => {
|
|
200
207
|
// One path config-allowed, one config-ask → descriptor with only the ask path.
|
|
201
208
|
const resolver = makeResolver();
|
|
202
|
-
resolver.
|
|
203
|
-
(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
: makeCheckResult("ask"),
|
|
209
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
210
|
+
intentValues(intent).includes("/outside/a.ts")
|
|
211
|
+
? makeCheckResult("allow", { source: "special" })
|
|
212
|
+
: makeCheckResult("ask"),
|
|
207
213
|
);
|
|
208
214
|
const result = await describeGate(
|
|
209
215
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
@@ -220,11 +226,10 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
220
226
|
it("config-denied path makes worstCheck deny even when another path is ask", async () => {
|
|
221
227
|
// One path config-denied, one config-ask → descriptor with preCheck.state === "deny".
|
|
222
228
|
const resolver = makeResolver();
|
|
223
|
-
resolver.
|
|
224
|
-
(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
: makeCheckResult("ask"),
|
|
229
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
230
|
+
intentValues(intent).includes("/outside/a.ts")
|
|
231
|
+
? makeCheckResult("deny", { source: "special" })
|
|
232
|
+
: makeCheckResult("ask"),
|
|
228
233
|
);
|
|
229
234
|
const result = await describeGate(
|
|
230
235
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
@@ -241,11 +246,10 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
241
246
|
|
|
242
247
|
it("only includes uncovered paths when some are session-covered", async () => {
|
|
243
248
|
const resolver = makeResolver();
|
|
244
|
-
resolver.
|
|
245
|
-
(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
: makeCheckResult("ask"),
|
|
249
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
250
|
+
intentValues(intent).includes("/outside/a.ts")
|
|
251
|
+
? makeCheckResult("allow", { source: "session" })
|
|
252
|
+
: makeCheckResult("ask"),
|
|
249
253
|
);
|
|
250
254
|
const result = await describeGate(
|
|
251
255
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
@@ -10,7 +10,6 @@ vi.mock("node:os", () => {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
import { BashProgram } from "#src/access-intent/bash/program";
|
|
13
|
-
import { getNonEmptyString, toRecord } from "#src/common";
|
|
14
13
|
import { describeBashPathGate } from "#src/handlers/gates/bash-path";
|
|
15
14
|
import type {
|
|
16
15
|
GateBypass,
|
|
@@ -20,6 +19,7 @@ import type {
|
|
|
20
19
|
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
21
20
|
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
22
21
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
22
|
+
import { getNonEmptyString, toRecord } from "#src/value-guards";
|
|
23
23
|
|
|
24
24
|
import {
|
|
25
25
|
makeGateCheckResult as makeCheckResult,
|
|
@@ -228,14 +228,16 @@ describe("describeBashPathGate", () => {
|
|
|
228
228
|
resolver,
|
|
229
229
|
)) as GateDescriptor;
|
|
230
230
|
|
|
231
|
-
expect(resolver.
|
|
232
|
-
|
|
231
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
232
|
+
kind: "path-values",
|
|
233
|
+
surface: "path",
|
|
234
|
+
values: [
|
|
233
235
|
"/test/project/nested/src/file.txt",
|
|
234
236
|
"nested/src/file.txt",
|
|
235
237
|
"src/file.txt",
|
|
236
238
|
],
|
|
237
|
-
undefined,
|
|
238
|
-
);
|
|
239
|
+
agentName: undefined,
|
|
240
|
+
});
|
|
239
241
|
// The raw token drives the prompt, denial context, and session approval.
|
|
240
242
|
expect(result.denialContext).toMatchObject({ pathValue: "src/file.txt" });
|
|
241
243
|
expect(result.decision.value).toBe("src/file.txt");
|
|
@@ -253,10 +255,12 @@ describe("describeBashPathGate", () => {
|
|
|
253
255
|
resolver,
|
|
254
256
|
);
|
|
255
257
|
|
|
256
|
-
expect(resolver.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
259
|
+
kind: "path-values",
|
|
260
|
+
surface: "path",
|
|
261
|
+
values: ["src/foo.ts"],
|
|
262
|
+
agentName: undefined,
|
|
263
|
+
});
|
|
260
264
|
});
|
|
261
265
|
|
|
262
266
|
it("binds a current-directory token's session approval to the cwd subtree", async () => {
|
|
@@ -30,11 +30,12 @@ describe("resolveExternalDirectoryPolicy", () => {
|
|
|
30
30
|
|
|
31
31
|
const result = resolveExternalDirectoryPolicy(path, resolver, undefined);
|
|
32
32
|
|
|
33
|
-
expect(resolver.
|
|
34
|
-
path
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
34
|
+
kind: "access-path",
|
|
35
|
+
surface: "external_directory",
|
|
36
|
+
path,
|
|
37
|
+
agentName: undefined,
|
|
38
|
+
});
|
|
38
39
|
expect(result).toEqual(makeCheckResult("ask"));
|
|
39
40
|
});
|
|
40
41
|
|
|
@@ -44,11 +45,12 @@ describe("resolveExternalDirectoryPolicy", () => {
|
|
|
44
45
|
|
|
45
46
|
resolveExternalDirectoryPolicy(path, resolver, "reviewer");
|
|
46
47
|
|
|
47
|
-
expect(resolver.
|
|
48
|
-
path
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
49
|
+
kind: "access-path",
|
|
50
|
+
surface: "external_directory",
|
|
51
|
+
path,
|
|
52
|
+
agentName: "reviewer",
|
|
53
|
+
});
|
|
52
54
|
});
|
|
53
55
|
});
|
|
54
56
|
|
|
@@ -74,12 +76,17 @@ describe("selectUncoveredExternalPaths", () => {
|
|
|
74
76
|
const allowed = AccessPath.forExternalDirectory("/outside/ok.ts", cwd);
|
|
75
77
|
const asked = AccessPath.forExternalDirectory("/outside/ask.ts", cwd);
|
|
76
78
|
const resolver = makeResolver();
|
|
77
|
-
resolver.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
?
|
|
81
|
-
:
|
|
82
|
-
|
|
79
|
+
resolver.resolve.mockImplementation((intent) => {
|
|
80
|
+
const values =
|
|
81
|
+
intent.kind === "access-path"
|
|
82
|
+
? intent.path.matchValues()
|
|
83
|
+
: intent.kind === "path-values"
|
|
84
|
+
? intent.values
|
|
85
|
+
: [];
|
|
86
|
+
return values.includes("/outside/ok.ts")
|
|
87
|
+
? makeCheckResult("allow")
|
|
88
|
+
: makeCheckResult("ask");
|
|
89
|
+
});
|
|
83
90
|
|
|
84
91
|
const { uncovered } = selectUncoveredExternalPaths(
|
|
85
92
|
[allowed, asked],
|
|
@@ -94,12 +101,17 @@ describe("selectUncoveredExternalPaths", () => {
|
|
|
94
101
|
const asked = AccessPath.forExternalDirectory("/outside/ask.ts", cwd);
|
|
95
102
|
const denied = AccessPath.forExternalDirectory("/outside/deny.ts", cwd);
|
|
96
103
|
const resolver = makeResolver();
|
|
97
|
-
resolver.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
?
|
|
101
|
-
:
|
|
102
|
-
|
|
104
|
+
resolver.resolve.mockImplementation((intent) => {
|
|
105
|
+
const values =
|
|
106
|
+
intent.kind === "access-path"
|
|
107
|
+
? intent.path.matchValues()
|
|
108
|
+
: intent.kind === "path-values"
|
|
109
|
+
? intent.values
|
|
110
|
+
: [];
|
|
111
|
+
return values.includes("/outside/deny.ts")
|
|
112
|
+
? makeCheckResult("deny")
|
|
113
|
+
: makeCheckResult("ask");
|
|
114
|
+
});
|
|
103
115
|
|
|
104
116
|
const { worstCheck } = selectUncoveredExternalPaths(
|
|
105
117
|
[asked, denied],
|
|
@@ -144,12 +144,18 @@ describe("describeExternalDirectoryGate", () => {
|
|
|
144
144
|
undefined,
|
|
145
145
|
resolver,
|
|
146
146
|
);
|
|
147
|
-
expect(resolver.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
148
|
+
expect.objectContaining({
|
|
149
|
+
kind: "access-path",
|
|
150
|
+
surface: "external_directory",
|
|
151
|
+
agentName: undefined,
|
|
152
|
+
}),
|
|
151
153
|
);
|
|
152
|
-
|
|
154
|
+
const intent = resolver.resolve.mock.calls[0][0];
|
|
155
|
+
expect(intent.kind).toBe("access-path");
|
|
156
|
+
if (intent.kind === "access-path") {
|
|
157
|
+
expect(intent.path.matchValues()).toEqual(["/outside/project/file.ts"]);
|
|
158
|
+
}
|
|
153
159
|
});
|
|
154
160
|
|
|
155
161
|
it("sessionApproval uses deriveApprovalPattern", () => {
|
|
@@ -155,11 +155,12 @@ describe("describePathGate", () => {
|
|
|
155
155
|
it("resolves the path surface with the file path and agent name", () => {
|
|
156
156
|
const resolver = makeResolver(makeCheckResult({ state: "allow" }));
|
|
157
157
|
describePathGate(makeTcc({ agentName: "my-agent" }), resolver);
|
|
158
|
-
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
159
|
-
"
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
|
|
158
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
159
|
+
kind: "tool",
|
|
160
|
+
surface: "path",
|
|
161
|
+
input: { path: ".env" },
|
|
162
|
+
agentName: "my-agent",
|
|
163
|
+
});
|
|
163
164
|
});
|
|
164
165
|
});
|
|
165
166
|
|
|
@@ -187,11 +188,12 @@ describe("describePathGate — home-relative paths", () => {
|
|
|
187
188
|
toolName: "read",
|
|
188
189
|
pathValue: "~/.ssh/config",
|
|
189
190
|
});
|
|
190
|
-
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
192
|
+
kind: "tool",
|
|
193
|
+
surface: "path",
|
|
194
|
+
input: { path: "~/.ssh/config" },
|
|
195
|
+
agentName: undefined,
|
|
196
|
+
});
|
|
195
197
|
});
|
|
196
198
|
|
|
197
199
|
it("passes raw $HOME/... path to resolver and builds descriptor on deny", () => {
|
|
@@ -243,11 +245,12 @@ describe("describePathGate — extension and MCP tools (#352)", () => {
|
|
|
243
245
|
resolver,
|
|
244
246
|
);
|
|
245
247
|
expect(isGateDescriptor(result)).toBe(true);
|
|
246
|
-
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
247
|
-
"
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
249
|
+
kind: "tool",
|
|
250
|
+
surface: "path",
|
|
251
|
+
input: { path: ".env" },
|
|
252
|
+
agentName: undefined,
|
|
253
|
+
});
|
|
251
254
|
});
|
|
252
255
|
|
|
253
256
|
it("gates an MCP tool via arguments.path", () => {
|
|
@@ -259,11 +262,12 @@ describe("describePathGate — extension and MCP tools (#352)", () => {
|
|
|
259
262
|
resolver,
|
|
260
263
|
);
|
|
261
264
|
expect(isGateDescriptor(result)).toBe(true);
|
|
262
|
-
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
263
|
-
"
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
266
|
+
kind: "tool",
|
|
267
|
+
surface: "path",
|
|
268
|
+
input: { path: ".env" },
|
|
269
|
+
agentName: undefined,
|
|
270
|
+
});
|
|
267
271
|
});
|
|
268
272
|
|
|
269
273
|
it("uses a registered extractor's path for a custom-shaped tool", () => {
|
|
@@ -275,11 +279,12 @@ describe("describePathGate — extension and MCP tools (#352)", () => {
|
|
|
275
279
|
resolver,
|
|
276
280
|
extractorLookup("ffgrep", "target"),
|
|
277
281
|
);
|
|
278
|
-
expect(resolver.resolve).toHaveBeenCalledWith(
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
expect(resolver.resolve).toHaveBeenCalledWith({
|
|
283
|
+
kind: "tool",
|
|
284
|
+
surface: "path",
|
|
285
|
+
input: { path: "/etc/passwd" },
|
|
286
|
+
agentName: undefined,
|
|
287
|
+
});
|
|
283
288
|
});
|
|
284
289
|
|
|
285
290
|
it("returns null for an extension tool without a path", () => {
|
|
@@ -215,7 +215,12 @@ describe("GateRunner — descriptor path", () => {
|
|
|
215
215
|
const { runner, deps } = makeGateRunner();
|
|
216
216
|
const result = await runner.run(makeDescriptor(), "test-agent", "tc-1");
|
|
217
217
|
expect(result).toEqual({ action: "allow" });
|
|
218
|
-
expect(deps.resolve).toHaveBeenCalledWith(
|
|
218
|
+
expect(deps.resolve).toHaveBeenCalledWith({
|
|
219
|
+
kind: "tool",
|
|
220
|
+
surface: "read",
|
|
221
|
+
input: {},
|
|
222
|
+
agentName: "test-agent",
|
|
223
|
+
});
|
|
219
224
|
expect(deps.reporter.emitDecision).toHaveBeenCalledWith(
|
|
220
225
|
expect.objectContaining({
|
|
221
226
|
agentName: "test-agent",
|
|
@@ -198,8 +198,8 @@ describe("ToolCallGatePipeline", () => {
|
|
|
198
198
|
// block can only come from the path gate seeing the extracted path.
|
|
199
199
|
function pathDenyingResolver() {
|
|
200
200
|
const resolver = makeResolver();
|
|
201
|
-
resolver.resolve.mockImplementation((
|
|
202
|
-
surface === "path"
|
|
201
|
+
resolver.resolve.mockImplementation((intent) =>
|
|
202
|
+
intent.surface === "path"
|
|
203
203
|
? makeCheckResult({ state: "deny", matchedPattern: "*" })
|
|
204
204
|
: makeCheckResult(),
|
|
205
205
|
);
|
|
@@ -67,7 +67,7 @@ describe("handleInput", () => {
|
|
|
67
67
|
it("does not check permissions for non-skill input", async () => {
|
|
68
68
|
const { handler, permissionManager } = makeHandler();
|
|
69
69
|
await handler.handleInput(makeInputEvent("just a message"), makeCtx());
|
|
70
|
-
expect(permissionManager.
|
|
70
|
+
expect(permissionManager.check).not.toHaveBeenCalled();
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it("returns continue when skill is allowed", async () => {
|