@gotgenes/pi-permission-system 5.17.0 → 5.18.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 +17 -0
- package/README.md +1 -1
- package/package.json +4 -1
- package/src/index.ts +21 -0
- package/src/input-normalizer.ts +28 -0
- package/src/permission-event-rpc.ts +1 -20
- package/src/permission-events.ts +25 -3
- package/src/service.ts +75 -0
- package/tests/service.test.ts +144 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ 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.18.0](https://github.com/gotgenes/pi-permission-system/compare/v5.17.0...v5.18.0) (2026-05-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add package.json exports field for cross-extension import ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([1091de5](https://github.com/gotgenes/pi-permission-system/commit/1091de5eb673050c3b83448ee69cffb876c407d9))
|
|
14
|
+
* add Symbol.for()-backed service accessor module ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([6a7ddab](https://github.com/gotgenes/pi-permission-system/commit/6a7ddab6e3e58ca0f93807255e9e48716d96ca24))
|
|
15
|
+
* publish permissions service on startup, clear on shutdown ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([97bea7b](https://github.com/gotgenes/pi-permission-system/commit/97bea7bec043de4f6abb823846cef5c04a069517))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
* deprecate permissions:rpc:check types in favor of service accessor ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([a64b1b9](https://github.com/gotgenes/pi-permission-system/commit/a64b1b91f141e1a98b576433280ad09d95ec3011))
|
|
21
|
+
* document service accessor and deprecate RPC check ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([931a14e](https://github.com/gotgenes/pi-permission-system/commit/931a14efec1ef9bc53075f8974bfd1eeff7e0749))
|
|
22
|
+
* plan Symbol.for()-backed service accessor ([#145](https://github.com/gotgenes/pi-permission-system/issues/145)) ([d9448bc](https://github.com/gotgenes/pi-permission-system/commit/d9448bc8c9ee71714599a18f03cf516a5ffca2cb))
|
|
23
|
+
* **retro:** add retro notes for issue [#148](https://github.com/gotgenes/pi-permission-system/issues/148) ([84e0262](https://github.com/gotgenes/pi-permission-system/commit/84e026264e292357c18c0333b1d1bd561f70149b))
|
|
24
|
+
|
|
8
25
|
## [5.17.0](https://github.com/gotgenes/pi-permission-system/compare/v5.16.0...v5.17.0) (2026-05-14)
|
|
9
26
|
|
|
10
27
|
|
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ For the full reference — all surfaces, runtime knobs, per-agent overrides, mer
|
|
|
91
91
|
|---|---|
|
|
92
92
|
|[docs/configuration.md](docs/configuration.md)|Full policy reference, runtime knobs, per-agent overrides, recipes|
|
|
93
93
|
|[docs/session-approvals.md](docs/session-approvals.md)|Session-scoped rules, pattern suggestions, bash arity table|
|
|
94
|
-
|[docs/
|
|
94
|
+
|[docs/cross-extension-api.md](docs/cross-extension-api.md)|Cross-extension service accessor, event bus integration, decision broadcasts|
|
|
95
95
|
|[docs/subagent-integration.md](docs/subagent-integration.md)|Permission forwarding, coexistence with subagent extensions|
|
|
96
96
|
|[docs/guides/permission-frontmatter-for-subagent-extensions.md](docs/guides/permission-frontmatter-for-subagent-extensions.md)|Convention guide for subagent extension authors|
|
|
97
97
|
|[docs/opencode-compatibility.md](docs/opencode-compatibility.md)|OpenCode compatibility — shared concepts, divergences, porting guide|
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotgenes/pi-permission-system",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.0",
|
|
4
4
|
"description": "Permission enforcement extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/service.ts"
|
|
8
|
+
},
|
|
6
9
|
"files": [
|
|
7
10
|
"src",
|
|
8
11
|
"tests",
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PermissionGateHandler,
|
|
9
9
|
SessionLifecycleHandler,
|
|
10
10
|
} from "./handlers";
|
|
11
|
+
import { buildInputForSurface } from "./input-normalizer";
|
|
11
12
|
import { requestPermissionDecisionFromUi } from "./permission-dialog";
|
|
12
13
|
import { registerPermissionRpcHandlers } from "./permission-event-rpc";
|
|
13
14
|
import { emitReadyEvent } from "./permission-events";
|
|
@@ -19,6 +20,11 @@ import {
|
|
|
19
20
|
refreshExtensionConfig,
|
|
20
21
|
saveExtensionConfig,
|
|
21
22
|
} from "./runtime";
|
|
23
|
+
import type { PermissionsService } from "./service";
|
|
24
|
+
import {
|
|
25
|
+
publishPermissionsService,
|
|
26
|
+
unpublishPermissionsService,
|
|
27
|
+
} from "./service";
|
|
22
28
|
import { createSessionLogger } from "./session-logger";
|
|
23
29
|
import { isSubagentExecutionContext } from "./subagent-context";
|
|
24
30
|
import {
|
|
@@ -91,6 +97,20 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
91
97
|
writeReviewLog: runtime.writeReviewLog.bind(runtime),
|
|
92
98
|
});
|
|
93
99
|
|
|
100
|
+
const permissionsService: PermissionsService = {
|
|
101
|
+
checkPermission(surface, value, agentName) {
|
|
102
|
+
const input = buildInputForSurface(surface, value);
|
|
103
|
+
const sessionRules = runtime.sessionRules.getRuleset();
|
|
104
|
+
return runtime.permissionManager.checkPermission(
|
|
105
|
+
surface,
|
|
106
|
+
input,
|
|
107
|
+
agentName,
|
|
108
|
+
sessionRules,
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
publishPermissionsService(permissionsService);
|
|
113
|
+
|
|
94
114
|
emitReadyEvent(pi.events);
|
|
95
115
|
|
|
96
116
|
const toolRegistry = {
|
|
@@ -101,6 +121,7 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
101
121
|
const lifecycle = new SessionLifecycleHandler(session, () => {
|
|
102
122
|
rpcHandles.unsubCheck();
|
|
103
123
|
rpcHandles.unsubPrompt();
|
|
124
|
+
unpublishPermissionsService();
|
|
104
125
|
});
|
|
105
126
|
const agentPrep = new AgentPrepHandler(session, toolRegistry);
|
|
106
127
|
const gates = new PermissionGateHandler(session, pi.events, toolRegistry);
|
package/src/input-normalizer.ts
CHANGED
|
@@ -2,6 +2,34 @@ import { toRecord } from "./common";
|
|
|
2
2
|
import { createMcpPermissionTargets } from "./mcp-targets";
|
|
3
3
|
import { getPathBearingToolPath, PATH_BEARING_TOOLS } from "./path-utils";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Construct a surface-appropriate input object from a raw value string.
|
|
7
|
+
*
|
|
8
|
+
* This is the inverse of `normalizeInput()` — it builds the minimal input
|
|
9
|
+
* object that `PermissionManager.checkPermission()` expects for a given
|
|
10
|
+
* surface, from a single string value.
|
|
11
|
+
*
|
|
12
|
+
* Used by the event-bus RPC handler and the `Symbol.for()` service accessor
|
|
13
|
+
* so external callers can query policy with `(surface, value)` instead of
|
|
14
|
+
* constructing a full tool-call input payload.
|
|
15
|
+
*
|
|
16
|
+
* Note: MCP inputs are complex (server name + tool name derivation). Callers
|
|
17
|
+
* providing an MCP surface receive a best-effort policy evaluation using the
|
|
18
|
+
* value as a pre-qualified target string. Pass the fully-qualified target
|
|
19
|
+
* (e.g. "exa:search" or "exa") directly.
|
|
20
|
+
*/
|
|
21
|
+
export function buildInputForSurface(
|
|
22
|
+
surface: string,
|
|
23
|
+
value: string | undefined,
|
|
24
|
+
): unknown {
|
|
25
|
+
const v = value ?? "";
|
|
26
|
+
if (surface === "bash") return { command: v };
|
|
27
|
+
if (surface === "skill") return { name: v };
|
|
28
|
+
if (surface === "external_directory") return { path: v };
|
|
29
|
+
// MCP and tool surfaces: normalizeInput handles them from the surface alone.
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
/**
|
|
6
34
|
* Surface-normalized representation of a tool invocation used by
|
|
7
35
|
* `checkPermission()` to feed a single `evaluateFirst()` call.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* permission prompts without importing this package.
|
|
7
7
|
*/
|
|
8
8
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { buildInputForSurface } from "./input-normalizer";
|
|
9
10
|
import type {
|
|
10
11
|
PermissionPromptDecision,
|
|
11
12
|
RequestPermissionOptions,
|
|
@@ -79,26 +80,6 @@ function errorReply(error: string): PermissionsRpcReply {
|
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
/**
|
|
83
|
-
* Construct a surface-appropriate input object from a raw value string.
|
|
84
|
-
*
|
|
85
|
-
* Note: MCP inputs are complex (server name + tool name derivation). Callers
|
|
86
|
-
* providing an MCP surface receive a best-effort policy evaluation using the
|
|
87
|
-
* value as a pre-qualified target string. Pass the fully-qualified target
|
|
88
|
-
* (e.g. "exa:search" or "exa") directly.
|
|
89
|
-
*/
|
|
90
|
-
function buildInputForSurface(
|
|
91
|
-
surface: string,
|
|
92
|
-
value: string | undefined,
|
|
93
|
-
): unknown {
|
|
94
|
-
const v = value ?? "";
|
|
95
|
-
if (surface === "bash") return { command: v };
|
|
96
|
-
if (surface === "skill") return { name: v };
|
|
97
|
-
if (surface === "external_directory") return { path: v };
|
|
98
|
-
// MCP and tool surfaces: normalizeInput handles them from the surface alone.
|
|
99
|
-
return {};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
83
|
// ── RPC handler: permissions:rpc:check ────────────────────────────────────
|
|
103
84
|
|
|
104
85
|
function handleCheckRpc(
|
package/src/permission-events.ts
CHANGED
|
@@ -30,7 +30,19 @@ export const PERMISSIONS_READY_CHANNEL = "permissions:ready";
|
|
|
30
30
|
/** Emitted after every permission gate resolution. */
|
|
31
31
|
export const PERMISSIONS_DECISION_CHANNEL = "permissions:decision";
|
|
32
32
|
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* RPC request channel — query the permission policy (no prompting).
|
|
35
|
+
*
|
|
36
|
+
* @deprecated Use the `Symbol.for()`-backed service accessor instead:
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const { getPermissionsService } = await import("@gotgenes/pi-permission-system");
|
|
39
|
+
* const service = getPermissionsService();
|
|
40
|
+
* if (service) {
|
|
41
|
+
* const result = service.checkPermission("bash", "git push");
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
* The event-bus RPC remains available as a zero-dependency fallback.
|
|
45
|
+
*/
|
|
34
46
|
export const PERMISSIONS_RPC_CHECK_CHANNEL = "permissions:rpc:check";
|
|
35
47
|
|
|
36
48
|
/** RPC request channel — forward a permission prompt to the parent UI. */
|
|
@@ -88,7 +100,12 @@ export interface PermissionDecisionEvent {
|
|
|
88
100
|
|
|
89
101
|
// ── permissions:rpc:check ──────────────────────────────────────────────────
|
|
90
102
|
|
|
91
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Request payload for `permissions:rpc:check`.
|
|
105
|
+
*
|
|
106
|
+
* @deprecated Prefer `getPermissionsService().checkPermission()` from the
|
|
107
|
+
* service accessor module. See `PERMISSIONS_RPC_CHECK_CHANNEL` for details.
|
|
108
|
+
*/
|
|
92
109
|
export interface PermissionsCheckRequest {
|
|
93
110
|
requestId: string;
|
|
94
111
|
/** Permission surface to evaluate. */
|
|
@@ -99,7 +116,12 @@ export interface PermissionsCheckRequest {
|
|
|
99
116
|
agentName?: string;
|
|
100
117
|
}
|
|
101
118
|
|
|
102
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Data field in a successful `permissions:rpc:check` reply.
|
|
121
|
+
*
|
|
122
|
+
* @deprecated Prefer `getPermissionsService().checkPermission()` from the
|
|
123
|
+
* service accessor module. See `PERMISSIONS_RPC_CHECK_CHANNEL` for details.
|
|
124
|
+
*/
|
|
103
125
|
export interface PermissionsCheckReplyData {
|
|
104
126
|
result: "allow" | "deny" | "ask";
|
|
105
127
|
matchedPattern: string | null;
|
package/src/service.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-extension service accessor backed by `Symbol.for()` on `globalThis`.
|
|
3
|
+
*
|
|
4
|
+
* `Symbol.for()` is process-global by spec, so it survives jiti's per-extension
|
|
5
|
+
* module isolation (`moduleCache: false`). A consumer doing
|
|
6
|
+
* `import("@gotgenes/pi-permission-system")` gets a fresh module copy, but
|
|
7
|
+
* `getPermissionsService()` reads from the same `globalThis` slot the provider
|
|
8
|
+
* wrote to — enabling direct, synchronous, type-safe function calls.
|
|
9
|
+
*
|
|
10
|
+
* Best practice: call `getPermissionsService()` per use rather than caching the
|
|
11
|
+
* reference — this ensures resilience across `/reload` and load-order edge cases.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { PermissionCheckResult, PermissionState } from "./types";
|
|
15
|
+
|
|
16
|
+
export type { PermissionCheckResult, PermissionState };
|
|
17
|
+
|
|
18
|
+
/** Process-global key for the service slot. */
|
|
19
|
+
const SERVICE_KEY = Symbol.for("@gotgenes/pi-permission-system:service");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Public interface exposed to other extensions via `getPermissionsService()`.
|
|
23
|
+
*
|
|
24
|
+
* Mirrors the simplified RPC signature — surface + optional value + optional
|
|
25
|
+
* agent name — and delegates to `PermissionManager.checkPermission()` with
|
|
26
|
+
* current session rules internally.
|
|
27
|
+
*/
|
|
28
|
+
export interface PermissionsService {
|
|
29
|
+
/**
|
|
30
|
+
* Query the permission policy for a surface and value.
|
|
31
|
+
*
|
|
32
|
+
* @param surface - Permission surface: "bash", "read", "mcp", "skill",
|
|
33
|
+
* "external_directory", etc.
|
|
34
|
+
* @param value - The value to evaluate: command string, tool name, skill
|
|
35
|
+
* name, or path. Omit or pass `undefined` for a
|
|
36
|
+
* surface-level query.
|
|
37
|
+
* @param agentName - Optional agent name for per-agent policy resolution.
|
|
38
|
+
* @returns Full check result including state, matched pattern, and origin.
|
|
39
|
+
*/
|
|
40
|
+
checkPermission(
|
|
41
|
+
surface: string,
|
|
42
|
+
value?: string,
|
|
43
|
+
agentName?: string,
|
|
44
|
+
): PermissionCheckResult;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Store a `PermissionsService` on `globalThis` so other extensions can
|
|
49
|
+
* retrieve it via `getPermissionsService()`.
|
|
50
|
+
*
|
|
51
|
+
* Overwrites any previously published service — safe for `/reload`.
|
|
52
|
+
*/
|
|
53
|
+
export function publishPermissionsService(service: PermissionsService): void {
|
|
54
|
+
(globalThis as Record<symbol, unknown>)[SERVICE_KEY] = service;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Retrieve the published `PermissionsService`, or `undefined` if the
|
|
59
|
+
* permission-system extension has not loaded (or has been unloaded).
|
|
60
|
+
*/
|
|
61
|
+
export function getPermissionsService(): PermissionsService | undefined {
|
|
62
|
+
return (globalThis as Record<symbol, unknown>)[SERVICE_KEY] as
|
|
63
|
+
| PermissionsService
|
|
64
|
+
| undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Remove the service from `globalThis`.
|
|
69
|
+
*
|
|
70
|
+
* Called during `session_shutdown` to avoid stale references after the
|
|
71
|
+
* extension is torn down.
|
|
72
|
+
*/
|
|
73
|
+
export function unpublishPermissionsService(): void {
|
|
74
|
+
delete (globalThis as Record<symbol, unknown>)[SERVICE_KEY];
|
|
75
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { buildInputForSurface } from "../src/input-normalizer";
|
|
3
|
+
import type { PermissionsService } from "../src/service";
|
|
4
|
+
import {
|
|
5
|
+
getPermissionsService,
|
|
6
|
+
publishPermissionsService,
|
|
7
|
+
unpublishPermissionsService,
|
|
8
|
+
} from "../src/service";
|
|
9
|
+
import type { PermissionCheckResult } from "../src/types";
|
|
10
|
+
|
|
11
|
+
// ── helpers ────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
function makeService(
|
|
14
|
+
overrides: Partial<PermissionsService> = {},
|
|
15
|
+
): PermissionsService {
|
|
16
|
+
return {
|
|
17
|
+
checkPermission: vi.fn(),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── globalThis accessor ────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
describe("globalThis accessor", () => {
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
unpublishPermissionsService();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns undefined when nothing has been published", () => {
|
|
30
|
+
expect(getPermissionsService()).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns the published service", () => {
|
|
34
|
+
const service = makeService();
|
|
35
|
+
publishPermissionsService(service);
|
|
36
|
+
expect(getPermissionsService()).toBe(service);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("overwrites a previously published service", () => {
|
|
40
|
+
const first = makeService();
|
|
41
|
+
const second = makeService();
|
|
42
|
+
publishPermissionsService(first);
|
|
43
|
+
publishPermissionsService(second);
|
|
44
|
+
expect(getPermissionsService()).toBe(second);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns undefined after unpublish", () => {
|
|
48
|
+
const service = makeService();
|
|
49
|
+
publishPermissionsService(service);
|
|
50
|
+
unpublishPermissionsService();
|
|
51
|
+
expect(getPermissionsService()).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("unpublish is safe to call when nothing was published", () => {
|
|
55
|
+
expect(() => unpublishPermissionsService()).not.toThrow();
|
|
56
|
+
expect(getPermissionsService()).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ── service adapter delegation ─────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
describe("service adapter delegation", () => {
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
unpublishPermissionsService();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const fakeResult: PermissionCheckResult = {
|
|
68
|
+
toolName: "bash",
|
|
69
|
+
state: "allow",
|
|
70
|
+
matchedPattern: "git *",
|
|
71
|
+
source: "bash",
|
|
72
|
+
origin: "global",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
it("checkPermission delegates surface and value through buildInputForSurface", () => {
|
|
76
|
+
const checkPermission = vi.fn().mockReturnValue(fakeResult);
|
|
77
|
+
const sessionRules = [
|
|
78
|
+
{
|
|
79
|
+
surface: "bash",
|
|
80
|
+
pattern: "*",
|
|
81
|
+
action: "allow" as const,
|
|
82
|
+
layer: "session" as const,
|
|
83
|
+
origin: "session" as const,
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Build the adapter the same way index.ts will
|
|
88
|
+
const service: PermissionsService = {
|
|
89
|
+
checkPermission(surface, value, agentName) {
|
|
90
|
+
const input = buildInputForSurface(surface, value);
|
|
91
|
+
return checkPermission(surface, input, agentName, sessionRules);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
publishPermissionsService(service);
|
|
96
|
+
const retrieved = getPermissionsService()!;
|
|
97
|
+
const result = retrieved.checkPermission("bash", "git push");
|
|
98
|
+
|
|
99
|
+
expect(result).toBe(fakeResult);
|
|
100
|
+
expect(checkPermission).toHaveBeenCalledWith(
|
|
101
|
+
"bash",
|
|
102
|
+
{ command: "git push" },
|
|
103
|
+
undefined,
|
|
104
|
+
sessionRules,
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("checkPermission passes agentName through", () => {
|
|
109
|
+
const checkPermission = vi.fn().mockReturnValue(fakeResult);
|
|
110
|
+
|
|
111
|
+
const service: PermissionsService = {
|
|
112
|
+
checkPermission(surface, value, agentName) {
|
|
113
|
+
const input = buildInputForSurface(surface, value);
|
|
114
|
+
return checkPermission(surface, input, agentName, []);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
publishPermissionsService(service);
|
|
119
|
+
getPermissionsService()!.checkPermission("skill", "my-skill", "Explore");
|
|
120
|
+
|
|
121
|
+
expect(checkPermission).toHaveBeenCalledWith(
|
|
122
|
+
"skill",
|
|
123
|
+
{ name: "my-skill" },
|
|
124
|
+
"Explore",
|
|
125
|
+
[],
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("checkPermission uses empty object for unknown surfaces", () => {
|
|
130
|
+
const checkPermission = vi.fn().mockReturnValue(fakeResult);
|
|
131
|
+
|
|
132
|
+
const service: PermissionsService = {
|
|
133
|
+
checkPermission(surface, value, agentName) {
|
|
134
|
+
const input = buildInputForSurface(surface, value);
|
|
135
|
+
return checkPermission(surface, input, agentName, []);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
publishPermissionsService(service);
|
|
140
|
+
getPermissionsService()!.checkPermission("read", "/tmp/file");
|
|
141
|
+
|
|
142
|
+
expect(checkPermission).toHaveBeenCalledWith("read", {}, undefined, []);
|
|
143
|
+
});
|
|
144
|
+
});
|