@gotgenes/pi-permission-system 10.0.0 → 10.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/agent-prep-session.ts +28 -0
  5. package/src/decision-reporter.ts +41 -0
  6. package/src/denial-messages.ts +11 -0
  7. package/src/forwarded-permissions/permission-forwarder.ts +549 -0
  8. package/src/forwarding-manager.ts +3 -7
  9. package/src/gate-handler-session.ts +13 -0
  10. package/src/gate-prompter.ts +14 -0
  11. package/src/handlers/before-agent-start.ts +2 -3
  12. package/src/handlers/gates/bash-command.ts +4 -18
  13. package/src/handlers/gates/bash-external-directory.ts +3 -15
  14. package/src/handlers/gates/bash-path.ts +3 -16
  15. package/src/handlers/gates/descriptor.ts +0 -28
  16. package/src/handlers/gates/path.ts +3 -15
  17. package/src/handlers/gates/runner.ts +142 -105
  18. package/src/handlers/gates/skill-input-gate-pipeline.ts +104 -0
  19. package/src/handlers/gates/skill-input.ts +44 -0
  20. package/src/handlers/gates/tool-call-gate-pipeline.ts +120 -0
  21. package/src/handlers/lifecycle.ts +9 -9
  22. package/src/handlers/permission-gate-handler.ts +34 -238
  23. package/src/index.ts +49 -69
  24. package/src/mcp-targets.ts +56 -46
  25. package/src/permission-prompter.ts +7 -58
  26. package/src/permission-resolver.ts +17 -0
  27. package/src/permission-session.ts +77 -9
  28. package/src/permissions-service.ts +53 -0
  29. package/src/service-lifecycle.ts +49 -0
  30. package/src/session-approval-recorder.ts +6 -0
  31. package/src/session-lifecycle-session.ts +24 -0
  32. package/src/tool-input-preview.ts +0 -62
  33. package/src/tool-input-prompt-formatters.ts +63 -0
  34. package/src/tool-preview-formatter.ts +6 -4
  35. package/test/decision-reporter.test.ts +112 -0
  36. package/test/denial-messages.test.ts +62 -0
  37. package/test/forwarding-manager.test.ts +26 -44
  38. package/test/handlers/before-agent-start.test.ts +45 -21
  39. package/test/handlers/external-directory-integration.test.ts +86 -22
  40. package/test/handlers/external-directory-session-dedup.test.ts +102 -55
  41. package/test/handlers/gates/bash-command.test.ts +49 -90
  42. package/test/handlers/gates/bash-external-directory.test.ts +54 -95
  43. package/test/handlers/gates/bash-path.test.ts +63 -148
  44. package/test/handlers/gates/path.test.ts +38 -105
  45. package/test/handlers/gates/runner.test.ts +150 -93
  46. package/test/handlers/gates/skill-input-gate-pipeline.test.ts +176 -0
  47. package/test/handlers/gates/skill-input.test.ts +128 -0
  48. package/test/handlers/gates/tool-call-gate-pipeline.test.ts +180 -0
  49. package/test/handlers/input.test.ts +1 -2
  50. package/test/handlers/lifecycle.test.ts +49 -33
  51. package/test/handlers/tool-call-events.test.ts +1 -1
  52. package/test/helpers/gate-fixtures.ts +147 -16
  53. package/test/helpers/handler-fixtures.ts +143 -27
  54. package/test/mcp-targets.test.ts +55 -0
  55. package/test/permission-forwarder.test.ts +295 -0
  56. package/test/permission-forwarding.test.ts +0 -282
  57. package/test/permission-prompter.test.ts +33 -44
  58. package/test/permission-session.test.ts +160 -27
  59. package/test/permissions-service.test.ts +151 -0
  60. package/test/runtime.test.ts +0 -4
  61. package/test/service-lifecycle.test.ts +162 -0
  62. package/test/tool-input-preview.test.ts +0 -111
  63. package/test/tool-input-prompt-formatters.test.ts +115 -0
  64. package/src/forwarded-permissions/polling.ts +0 -411
@@ -1,13 +1,14 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
 
3
3
  import type { GateDescriptor } from "#src/handlers/gates/descriptor";
4
4
  import { isGateDescriptor } from "#src/handlers/gates/descriptor";
5
5
  import { describePathGate } from "#src/handlers/gates/path";
6
6
  import type { ToolCallContext } from "#src/handlers/gates/types";
7
- import type { Rule } from "#src/rule";
8
- import type { PermissionCheckResult } from "#src/types";
9
7
 
10
- import { makeGateCheckResult as makeCheckResult } from "#test/helpers/gate-fixtures";
8
+ import {
9
+ makeGateCheckResult as makeCheckResult,
10
+ makeResolver,
11
+ } from "#test/helpers/gate-fixtures";
11
12
 
12
13
  // ── helpers ────────────────────────────────────────────────────────────────
13
14
 
@@ -23,53 +24,36 @@ function makeTcc(overrides: Partial<ToolCallContext> = {}): ToolCallContext {
23
24
  };
24
25
  }
25
26
 
26
- type CheckPermissionFn = (
27
- surface: string,
28
- input: unknown,
29
- agentName?: string,
30
- sessionRules?: Rule[],
31
- ) => PermissionCheckResult;
32
-
33
27
  // ── tests ──────────────────────────────────────────────────────────────────
34
28
 
35
29
  describe("describePathGate", () => {
36
- const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
37
-
38
30
  it("returns null for non-path-bearing tools", () => {
39
- const checkPermission = vi.fn<CheckPermissionFn>();
31
+ const resolver = makeResolver();
40
32
  const result = describePathGate(
41
33
  makeTcc({ toolName: "bash", input: { command: "ls" } }),
42
- checkPermission,
43
- getSessionRuleset,
34
+ resolver,
44
35
  );
45
36
  expect(result).toBeNull();
46
- expect(checkPermission).not.toHaveBeenCalled();
37
+ expect(resolver.resolve).not.toHaveBeenCalled();
47
38
  });
48
39
 
49
40
  it("returns null when tool has no extractable path", () => {
50
- const checkPermission = vi.fn<CheckPermissionFn>();
41
+ const resolver = makeResolver();
51
42
  const result = describePathGate(
52
43
  makeTcc({ toolName: "read", input: {} }),
53
- checkPermission,
54
- getSessionRuleset,
44
+ resolver,
55
45
  );
56
46
  expect(result).toBeNull();
57
47
  });
58
48
 
59
49
  it("returns null when path check result is allow", () => {
60
- const checkPermission = vi
61
- .fn<CheckPermissionFn>()
62
- .mockReturnValue(makeCheckResult({ state: "allow" }));
63
- const result = describePathGate(
64
- makeTcc(),
65
- checkPermission,
66
- getSessionRuleset,
67
- );
50
+ const resolver = makeResolver(makeCheckResult({ state: "allow" }));
51
+ const result = describePathGate(makeTcc(), resolver);
68
52
  expect(result).toBeNull();
69
53
  });
70
54
 
71
55
  it("returns null when matchedPattern is undefined (universal default)", () => {
72
- const checkPermission = vi.fn<CheckPermissionFn>().mockReturnValue(
56
+ const resolver = makeResolver(
73
57
  makeCheckResult({
74
58
  state: "ask",
75
59
  matchedPattern: undefined,
@@ -77,16 +61,12 @@ describe("describePathGate", () => {
77
61
  origin: "builtin",
78
62
  }),
79
63
  );
80
- const result = describePathGate(
81
- makeTcc(),
82
- checkPermission,
83
- getSessionRuleset,
84
- );
64
+ const result = describePathGate(makeTcc(), resolver);
85
65
  expect(result).toBeNull();
86
66
  });
87
67
 
88
68
  it("returns GateDescriptor when matchedPattern is defined (explicit path rule)", () => {
89
- const checkPermission = vi.fn<CheckPermissionFn>().mockReturnValue(
69
+ const resolver = makeResolver(
90
70
  makeCheckResult({
91
71
  state: "ask",
92
72
  matchedPattern: "*.env",
@@ -94,27 +74,16 @@ describe("describePathGate", () => {
94
74
  origin: "global",
95
75
  }),
96
76
  );
97
- const result = describePathGate(
98
- makeTcc(),
99
- checkPermission,
100
- getSessionRuleset,
101
- );
77
+ const result = describePathGate(makeTcc(), resolver);
102
78
  expect(result).not.toBeNull();
103
79
  expect(isGateDescriptor(result)).toBe(true);
104
80
  });
105
81
 
106
82
  it("returns GateDescriptor when path check result is deny", () => {
107
- const checkPermission = vi.fn<CheckPermissionFn>().mockReturnValue(
108
- makeCheckResult({
109
- state: "deny",
110
- matchedPattern: "*.env",
111
- }),
112
- );
113
- const result = describePathGate(
114
- makeTcc(),
115
- checkPermission,
116
- getSessionRuleset,
83
+ const resolver = makeResolver(
84
+ makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
117
85
  );
86
+ const result = describePathGate(makeTcc(), resolver);
118
87
  expect(result).not.toBeNull();
119
88
  expect(isGateDescriptor(result)).toBe(true);
120
89
  const desc = result as GateDescriptor;
@@ -123,17 +92,10 @@ describe("describePathGate", () => {
123
92
  });
124
93
 
125
94
  it("returns GateDescriptor when path check result is ask", () => {
126
- const checkPermission = vi.fn<CheckPermissionFn>().mockReturnValue(
127
- makeCheckResult({
128
- state: "ask",
129
- matchedPattern: "*.env",
130
- }),
131
- );
132
- const result = describePathGate(
133
- makeTcc(),
134
- checkPermission,
135
- getSessionRuleset,
95
+ const resolver = makeResolver(
96
+ makeCheckResult({ state: "ask", matchedPattern: "*.env" }),
136
97
  );
98
+ const result = describePathGate(makeTcc(), resolver);
137
99
  expect(result).not.toBeNull();
138
100
  expect(isGateDescriptor(result)).toBe(true);
139
101
  const desc = result as GateDescriptor;
@@ -142,13 +104,12 @@ describe("describePathGate", () => {
142
104
  });
143
105
 
144
106
  it("descriptor has correct session approval surface and pattern", () => {
145
- const checkPermission = vi
146
- .fn<CheckPermissionFn>()
147
- .mockReturnValue(makeCheckResult({ state: "ask", matchedPattern: "*" }));
107
+ const resolver = makeResolver(
108
+ makeCheckResult({ state: "ask", matchedPattern: "*" }),
109
+ );
148
110
  const result = describePathGate(
149
111
  makeTcc({ input: { path: "/test/project/src/.env" } }),
150
- checkPermission,
151
- getSessionRuleset,
112
+ resolver,
152
113
  ) as GateDescriptor;
153
114
  expect(result.sessionApproval).toBeDefined();
154
115
  expect(result.sessionApproval?.surface).toBe("path");
@@ -156,16 +117,10 @@ describe("describePathGate", () => {
156
117
  });
157
118
 
158
119
  it("descriptor denialContext references the file path and tool name", () => {
159
- const checkPermission = vi
160
- .fn<CheckPermissionFn>()
161
- .mockReturnValue(
162
- makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
163
- );
164
- const result = describePathGate(
165
- makeTcc(),
166
- checkPermission,
167
- getSessionRuleset,
168
- ) as GateDescriptor;
120
+ const resolver = makeResolver(
121
+ makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
122
+ );
123
+ const result = describePathGate(makeTcc(), resolver) as GateDescriptor;
169
124
  expect(result.denialContext).toEqual({
170
125
  kind: "path",
171
126
  toolName: "read",
@@ -175,43 +130,21 @@ describe("describePathGate", () => {
175
130
  });
176
131
 
177
132
  it("descriptor decision uses surface 'path' and the file path as value", () => {
178
- const checkPermission = vi
179
- .fn<CheckPermissionFn>()
180
- .mockReturnValue(
181
- makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
182
- );
183
- const result = describePathGate(
184
- makeTcc(),
185
- checkPermission,
186
- getSessionRuleset,
187
- ) as GateDescriptor;
133
+ const resolver = makeResolver(
134
+ makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
135
+ );
136
+ const result = describePathGate(makeTcc(), resolver) as GateDescriptor;
188
137
  expect(result.decision.surface).toBe("path");
189
138
  expect(result.decision.value).toBe(".env");
190
139
  });
191
140
 
192
- it("passes agentName and session rules to checkPermission", () => {
193
- const sessionRules: Rule[] = [
194
- {
195
- surface: "path",
196
- pattern: "/project/*",
197
- action: "allow",
198
- origin: "session",
199
- },
200
- ];
201
- const getSession = vi.fn<() => Rule[]>().mockReturnValue(sessionRules);
202
- const checkPermission = vi
203
- .fn<CheckPermissionFn>()
204
- .mockReturnValue(makeCheckResult({ state: "allow" }));
205
- describePathGate(
206
- makeTcc({ agentName: "my-agent" }),
207
- checkPermission,
208
- getSession,
209
- );
210
- expect(checkPermission).toHaveBeenCalledWith(
141
+ it("resolves the path surface with the file path and agent name", () => {
142
+ const resolver = makeResolver(makeCheckResult({ state: "allow" }));
143
+ describePathGate(makeTcc({ agentName: "my-agent" }), resolver);
144
+ expect(resolver.resolve).toHaveBeenCalledWith(
211
145
  "path",
212
146
  { path: ".env" },
213
147
  "my-agent",
214
- sessionRules,
215
148
  );
216
149
  });
217
150
  });