@gotgenes/pi-permission-system 7.1.3 → 7.2.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 +7 -3
- package/src/active-agent.ts +1 -1
- package/src/bash-arity.ts +1 -0
- package/src/config-modal.ts +2 -0
- package/src/forwarded-permissions/io.ts +4 -2
- package/src/forwarded-permissions/polling.ts +8 -7
- package/src/handlers/before-agent-start.ts +7 -6
- package/src/handlers/gates/bash-external-directory.ts +4 -4
- package/src/handlers/gates/bash-path-extractor.ts +4 -6
- package/src/handlers/gates/bash-path.ts +5 -5
- package/src/handlers/gates/descriptor.ts +6 -6
- package/src/handlers/gates/external-directory.ts +2 -2
- package/src/handlers/gates/helpers.ts +2 -2
- package/src/handlers/gates/path.ts +4 -4
- package/src/handlers/gates/runner.ts +7 -4
- package/src/handlers/gates/skill-read.ts +5 -5
- package/src/handlers/gates/tool.ts +5 -5
- package/src/handlers/lifecycle.ts +9 -8
- package/src/handlers/permission-gate-handler.ts +12 -7
- package/src/logging.ts +3 -0
- package/src/node-modules-discovery.ts +1 -1
- package/src/normalize.ts +1 -0
- package/src/permission-event-rpc.ts +2 -0
- package/src/permission-manager.ts +7 -6
- package/src/permission-merge.ts +4 -2
- package/src/permission-prompter.ts +3 -0
- package/src/permission-prompts.ts +1 -1
- package/src/policy-loader.ts +5 -5
- package/src/service.ts +1 -0
- package/src/skill-prompt-sanitizer.ts +3 -3
- package/src/tool-registry.ts +1 -1
- package/src/wildcard-matcher.ts +2 -2
- package/src/yolo-mode.ts +2 -1
- package/{tests → test}/active-agent.test.ts +1 -1
- package/{tests → test}/bash-arity.test.ts +4 -4
- package/{tests → test}/bash-external-directory.test.ts +3 -3
- package/{tests → test}/common.test.ts +1 -1
- package/{tests → test}/config-loader.test.ts +1 -1
- package/{tests → test}/config-modal.test.ts +9 -11
- package/{tests → test}/config-paths.test.ts +1 -1
- package/{tests → test}/config-reporter.test.ts +4 -4
- package/{tests → test}/denial-messages.test.ts +2 -2
- package/{tests → test}/expand-home.test.ts +1 -1
- package/{tests → test}/extension-config.test.ts +1 -1
- package/{tests → test}/extension-paths.test.ts +2 -2
- package/{tests → test}/forwarded-permissions/io.test.ts +2 -2
- package/{tests → test}/forwarding-manager.test.ts +1 -1
- package/{tests → test}/handlers/before-agent-start.test.ts +5 -5
- package/{tests → test}/handlers/external-directory-integration.test.ts +8 -8
- package/{tests → test}/handlers/external-directory-session-dedup.test.ts +6 -6
- package/{tests → test}/handlers/gates/bash-external-directory.test.ts +5 -8
- package/{tests → test}/handlers/gates/bash-path.test.ts +6 -9
- package/{tests → test}/handlers/gates/external-directory-messages.test.ts +1 -1
- package/{tests → test}/handlers/gates/external-directory.test.ts +4 -7
- package/{tests → test}/handlers/gates/helpers.test.ts +1 -1
- package/{tests → test}/handlers/gates/path.test.ts +6 -6
- package/{tests → test}/handlers/gates/runner.test.ts +5 -5
- package/{tests → test}/handlers/gates/skill-read.test.ts +12 -14
- package/{tests → test}/handlers/gates/tool.test.ts +4 -4
- package/{tests → test}/handlers/input-events.test.ts +7 -7
- package/{tests → test}/handlers/input.test.ts +5 -5
- package/{tests → test}/handlers/lifecycle.test.ts +2 -2
- package/{tests → test}/handlers/tool-call-events.test.ts +7 -7
- package/{tests → test}/handlers/tool-call.test.ts +5 -5
- package/{tests → test}/input-normalizer.test.ts +2 -2
- package/{tests → test}/mcp-targets.test.ts +1 -1
- package/{tests → test}/node-modules-discovery.test.ts +1 -1
- package/{tests → test}/normalize.test.ts +1 -1
- package/{tests → test}/path-utils.test.ts +1 -1
- package/{tests → test}/pattern-suggest.test.ts +1 -1
- package/{tests → test}/permission-dialog.test.ts +1 -1
- package/{tests → test}/permission-event-rpc.test.ts +4 -3
- package/{tests → test}/permission-events.test.ts +6 -4
- package/{tests → test}/permission-forwarding.test.ts +2 -1
- package/{tests → test}/permission-gate.test.ts +2 -2
- package/{tests → test}/permission-manager-unified.test.ts +9 -7
- package/{tests → test}/permission-merge.test.ts +1 -1
- package/{tests → test}/permission-prompter.test.ts +4 -4
- package/{tests → test}/permission-prompts.test.ts +4 -4
- package/{tests → test}/permission-session.test.ts +9 -9
- package/{tests → test}/permission-system.test.ts +21 -21
- package/{tests → test}/pi-infrastructure-read.test.ts +2 -2
- package/{tests → test}/policy-loader.test.ts +1 -1
- package/{tests → test}/rule.test.ts +2 -2
- package/{tests → test}/runtime.test.ts +4 -4
- package/{tests → test}/service.test.ts +4 -4
- package/{tests → test}/session-logger.test.ts +2 -2
- package/{tests → test}/session-rules.test.ts +2 -2
- package/{tests → test}/session-start.test.ts +4 -4
- package/{tests → test}/skill-prompt-sanitizer.test.ts +3 -3
- package/{tests → test}/subagent-context.test.ts +2 -2
- package/{tests → test}/synthesize.test.ts +3 -3
- package/{tests → test}/system-prompt-sanitizer.test.ts +1 -1
- package/{tests → test}/tool-input-preview.test.ts +3 -3
- package/{tests → test}/tool-registry.test.ts +1 -1
- package/{tests → test}/wildcard-matcher.test.ts +1 -1
- package/{tests → test}/yolo-mode.test.ts +2 -2
|
@@ -38,7 +38,7 @@ export function formatAskPrompt(
|
|
|
38
38
|
const patternInfo = result.matchedPattern
|
|
39
39
|
? ` (matched '${result.matchedPattern}')`
|
|
40
40
|
: "";
|
|
41
|
-
return `${subject} requested bash command '${result.command
|
|
41
|
+
return `${subject} requested bash command '${result.command ?? ""}'${patternInfo}. Allow this command?`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if ((result.source === "mcp" || result.toolName === "mcp") && result.target) {
|
package/src/policy-loader.ts
CHANGED
|
@@ -169,12 +169,12 @@ export class FilePolicyLoader implements PolicyLoader {
|
|
|
169
169
|
|
|
170
170
|
constructor(options: PolicyLoaderOptions = {}) {
|
|
171
171
|
this.globalConfigPath =
|
|
172
|
-
options.globalConfigPath
|
|
173
|
-
this.agentsDir = options.agentsDir
|
|
174
|
-
this.projectGlobalConfigPath = options.projectGlobalConfigPath
|
|
175
|
-
this.projectAgentsDir = options.projectAgentsDir
|
|
172
|
+
options.globalConfigPath ?? defaultGlobalConfigPath();
|
|
173
|
+
this.agentsDir = options.agentsDir ?? defaultAgentsDir();
|
|
174
|
+
this.projectGlobalConfigPath = options.projectGlobalConfigPath ?? null;
|
|
175
|
+
this.projectAgentsDir = options.projectAgentsDir ?? null;
|
|
176
176
|
this.globalMcpConfigPath =
|
|
177
|
-
options.globalMcpConfigPath
|
|
177
|
+
options.globalMcpConfigPath ?? defaultGlobalMcpConfigPath();
|
|
178
178
|
this.configuredMcpServerNamesOverride = options.mcpServerNames
|
|
179
179
|
? [
|
|
180
180
|
...new Set(
|
package/src/service.ts
CHANGED
|
@@ -71,5 +71,6 @@ export function getPermissionsService(): PermissionsService | undefined {
|
|
|
71
71
|
* extension is torn down.
|
|
72
72
|
*/
|
|
73
73
|
export function unpublishPermissionsService(): void {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property; Map.delete() is not applicable
|
|
74
75
|
delete (globalThis as Record<symbol, unknown>)[SERVICE_KEY];
|
|
75
76
|
}
|
|
@@ -70,9 +70,9 @@ function parseSkillEntries(sectionBody: string): ParsedSkillPromptEntry[] {
|
|
|
70
70
|
|
|
71
71
|
for (const match of sectionBody.matchAll(skillBlockRegex)) {
|
|
72
72
|
const block = match[1];
|
|
73
|
-
const nameMatch =
|
|
74
|
-
const descriptionMatch =
|
|
75
|
-
const locationMatch =
|
|
73
|
+
const nameMatch = SKILL_NAME_REGEX.exec(block);
|
|
74
|
+
const descriptionMatch = SKILL_DESCRIPTION_REGEX.exec(block);
|
|
75
|
+
const locationMatch = SKILL_LOCATION_REGEX.exec(block);
|
|
76
76
|
|
|
77
77
|
if (!nameMatch || !descriptionMatch || !locationMatch) {
|
|
78
78
|
continue;
|
package/src/tool-registry.ts
CHANGED
|
@@ -35,7 +35,7 @@ function buildReverseAliases(
|
|
|
35
35
|
const reverse = new Map<string, string[]>();
|
|
36
36
|
|
|
37
37
|
for (const [alias, canonical] of Object.entries(aliases)) {
|
|
38
|
-
const existing = reverse.get(canonical)
|
|
38
|
+
const existing = reverse.get(canonical) ?? [];
|
|
39
39
|
if (!existing.includes(alias)) {
|
|
40
40
|
existing.push(alias);
|
|
41
41
|
}
|
package/src/wildcard-matcher.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function compileWildcardPattern<TState>(
|
|
|
30
30
|
// space-and-arguments portion optional so that e.g. "git *" matches both
|
|
31
31
|
// "git status" and bare "git". Mirrors OpenCode wildcard semantics.
|
|
32
32
|
if (escaped.endsWith(" .*")) {
|
|
33
|
-
escaped = escaped.slice(0, -3)
|
|
33
|
+
escaped = `${escaped.slice(0, -3)}( .*)?`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
return {
|
|
@@ -48,7 +48,7 @@ export function compileWildcardPatternEntries<TState>(
|
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function
|
|
51
|
+
function _compileWildcardPatterns<TState>(
|
|
52
52
|
patterns: Record<string, TState>,
|
|
53
53
|
): CompiledWildcardPattern<TState>[] {
|
|
54
54
|
return compileWildcardPatternEntries(Object.entries(patterns));
|
package/src/yolo-mode.ts
CHANGED
|
@@ -10,7 +10,8 @@ export interface AskPermissionResolutionOptions {
|
|
|
10
10
|
export function isYoloModeEnabled(
|
|
11
11
|
config: PermissionSystemExtensionConfig,
|
|
12
12
|
): boolean {
|
|
13
|
-
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion -- typed as boolean but may be undefined at runtime (untyped callers); Boolean() guards against that
|
|
14
|
+
return Boolean(config.yoloMode);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function shouldAutoApprovePermissionState(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { ARITY, prefix } from "
|
|
2
|
+
import { ARITY, prefix } from "#src/bash-arity";
|
|
3
3
|
|
|
4
4
|
describe("ARITY dictionary", () => {
|
|
5
5
|
it("is exported as a plain object", () => {
|
|
@@ -7,7 +7,7 @@ describe("ARITY dictionary", () => {
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
it("maps 'git' to arity 2", () => {
|
|
10
|
-
expect(ARITY
|
|
10
|
+
expect(ARITY.git).toBe(2);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it("maps 'npm run' to arity 3", () => {
|
|
@@ -15,7 +15,7 @@ describe("ARITY dictionary", () => {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it("maps 'npm' to arity 2 (fallback when 'npm run' does not match)", () => {
|
|
18
|
-
expect(ARITY
|
|
18
|
+
expect(ARITY.npm).toBe(2);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it("maps 'docker compose' to arity 3", () => {
|
|
@@ -23,7 +23,7 @@ describe("ARITY dictionary", () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
it("maps 'docker' to arity 2 (fallback)", () => {
|
|
26
|
-
expect(ARITY
|
|
26
|
+
expect(ARITY.docker).toBe(2);
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -9,12 +9,12 @@ vi.mock("node:os", () => {
|
|
|
9
9
|
};
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
import { formatDenyReason } from "
|
|
12
|
+
import { formatDenyReason } from "#src/denial-messages";
|
|
13
13
|
import {
|
|
14
14
|
extractExternalPathsFromBashCommand,
|
|
15
15
|
extractTokensForPathRules,
|
|
16
|
-
} from "
|
|
17
|
-
import { formatBashExternalDirectoryAskPrompt } from "
|
|
16
|
+
} from "#src/handlers/gates/bash-path-extractor";
|
|
17
|
+
import { formatBashExternalDirectoryAskPrompt } from "#src/handlers/gates/external-directory-messages";
|
|
18
18
|
|
|
19
19
|
afterEach(() => {
|
|
20
20
|
vi.restoreAllMocks();
|
|
@@ -2,13 +2,13 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { expect, test, vi } from "vitest";
|
|
5
|
-
import { registerPermissionSystemCommand } from "
|
|
5
|
+
import { registerPermissionSystemCommand } from "#src/config-modal";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_EXTENSION_CONFIG,
|
|
8
8
|
normalizePermissionSystemConfig,
|
|
9
9
|
type PermissionSystemExtensionConfig,
|
|
10
|
-
} from "
|
|
11
|
-
import type { Rule } from "
|
|
10
|
+
} from "#src/extension-config";
|
|
11
|
+
import type { Rule } from "#src/rule";
|
|
12
12
|
|
|
13
13
|
vi.mock("@earendil-works/pi-coding-agent", () => ({
|
|
14
14
|
getSettingsListTheme: () => ({}),
|
|
@@ -68,7 +68,7 @@ function createCommandContext(hasUI: boolean): {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function lastNotification(notifications: Notification[]): Notification {
|
|
71
|
-
return notifications[notifications.length - 1]
|
|
71
|
+
return notifications[notifications.length - 1];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
test("permission-system command completions expose top-level config actions", () => {
|
|
@@ -101,7 +101,7 @@ test("permission-system command completions expose top-level config actions", ()
|
|
|
101
101
|
definition = nextDefinition;
|
|
102
102
|
},
|
|
103
103
|
} as never,
|
|
104
|
-
controller
|
|
104
|
+
controller,
|
|
105
105
|
);
|
|
106
106
|
|
|
107
107
|
expect(definition!.getArgumentCompletions).toBeTypeOf("function");
|
|
@@ -172,13 +172,11 @@ test("permission-system command handlers manage config summary, persistence, and
|
|
|
172
172
|
definition = nextDefinition;
|
|
173
173
|
},
|
|
174
174
|
} as never,
|
|
175
|
-
controller
|
|
175
|
+
controller,
|
|
176
176
|
);
|
|
177
177
|
|
|
178
178
|
expect(registeredName).toBe("permission-system");
|
|
179
|
-
expect(definition!.description
|
|
180
|
-
"Configure pi-permission-system",
|
|
181
|
-
);
|
|
179
|
+
expect(definition!.description).toContain("Configure pi-permission-system");
|
|
182
180
|
|
|
183
181
|
const infoCtx = createCommandContext(true);
|
|
184
182
|
await definition!.handler("show", infoCtx.ctx);
|
|
@@ -267,7 +265,7 @@ test("show output includes rule origins when getComposedRules is provided", asyn
|
|
|
267
265
|
definition = nextDef;
|
|
268
266
|
},
|
|
269
267
|
} as never,
|
|
270
|
-
controller
|
|
268
|
+
controller,
|
|
271
269
|
);
|
|
272
270
|
|
|
273
271
|
const ctx = createCommandContext(true);
|
|
@@ -300,7 +298,7 @@ test("show output omits rule summary when getComposedRules is not provided", asy
|
|
|
300
298
|
definition = nextDef;
|
|
301
299
|
},
|
|
302
300
|
} as never,
|
|
303
|
-
controller
|
|
301
|
+
controller,
|
|
304
302
|
);
|
|
305
303
|
|
|
306
304
|
const ctx = createCommandContext(true);
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { expect, test } from "vitest";
|
|
11
|
-
import { buildResolvedConfigLogEntry } from "
|
|
12
|
-
import { createPermissionSystemLogger } from "
|
|
13
|
-
import type { ResolvedPolicyPaths } from "
|
|
14
|
-
import { PermissionManager } from "
|
|
11
|
+
import { buildResolvedConfigLogEntry } from "#src/config-reporter";
|
|
12
|
+
import { createPermissionSystemLogger } from "#src/logging";
|
|
13
|
+
import type { ResolvedPolicyPaths } from "#src/permission-manager";
|
|
14
|
+
import { PermissionManager } from "#src/permission-manager";
|
|
15
15
|
|
|
16
16
|
test("buildResolvedConfigLogEntry includes policy paths and legacy detection flags", () => {
|
|
17
17
|
const policyPaths: ResolvedPolicyPaths = {
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
formatDenyReason,
|
|
7
7
|
formatUnavailableReason,
|
|
8
8
|
formatUserDeniedReason,
|
|
9
|
-
} from "
|
|
10
|
-
import type { PermissionCheckResult } from "
|
|
9
|
+
} from "#src/denial-messages";
|
|
10
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
11
11
|
|
|
12
12
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
13
13
|
|
|
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import {
|
|
4
4
|
detectMisplacedPermissionKeys,
|
|
5
5
|
normalizePermissionSystemConfig,
|
|
6
|
-
} from "
|
|
6
|
+
} from "#src/extension-config";
|
|
7
7
|
|
|
8
8
|
describe("detectMisplacedPermissionKeys", () => {
|
|
9
9
|
it("returns an empty array for a record with only valid extension keys", () => {
|
|
@@ -9,8 +9,8 @@ vi.mock("../src/node-modules-discovery", () => ({
|
|
|
9
9
|
discoverGlobalNodeModulesRoot: mockDiscoverGlobalNodeModulesRoot,
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
-
import { getGlobalLogsDir } from "
|
|
13
|
-
import { computeExtensionPaths } from "
|
|
12
|
+
import { getGlobalLogsDir } from "#src/config-paths";
|
|
13
|
+
import { computeExtensionPaths } from "#src/extension-paths";
|
|
14
14
|
|
|
15
15
|
describe("computeExtensionPaths", () => {
|
|
16
16
|
beforeEach(() => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import type { ForwardedPermissionLogger } from "
|
|
3
|
+
import type { ForwardedPermissionLogger } from "#src/forwarded-permissions/io";
|
|
4
4
|
import {
|
|
5
5
|
formatUnknownErrorMessage,
|
|
6
6
|
isErrnoCode,
|
|
7
7
|
logPermissionForwardingError,
|
|
8
8
|
logPermissionForwardingWarning,
|
|
9
|
-
} from "
|
|
9
|
+
} from "#src/forwarded-permissions/io";
|
|
10
10
|
|
|
11
11
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import { ForwardingManager } from "
|
|
3
|
+
import { ForwardingManager } from "#src/forwarding-manager";
|
|
4
4
|
|
|
5
5
|
// ── Mocks ─────────────────────────────────────────────────────────────────
|
|
6
6
|
|
|
@@ -4,10 +4,10 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
4
4
|
import {
|
|
5
5
|
AgentPrepHandler,
|
|
6
6
|
shouldExposeTool,
|
|
7
|
-
} from "
|
|
8
|
-
import type { PermissionSession } from "
|
|
9
|
-
import type { ToolRegistry } from "
|
|
10
|
-
import type { PermissionState } from "
|
|
7
|
+
} from "#src/handlers/before-agent-start";
|
|
8
|
+
import type { PermissionSession } from "#src/permission-session";
|
|
9
|
+
import type { ToolRegistry } from "#src/tool-registry";
|
|
10
|
+
import type { PermissionState } from "#src/types";
|
|
11
11
|
|
|
12
12
|
// ── SDK stubs ──────────────────────────────────────────────────────────────
|
|
13
13
|
vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
|
|
@@ -52,7 +52,7 @@ function makeSession(
|
|
|
52
52
|
activate: vi.fn(),
|
|
53
53
|
refreshConfig: vi.fn(),
|
|
54
54
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
55
|
-
getToolPermission: vi.fn().mockReturnValue("allow"
|
|
55
|
+
getToolPermission: vi.fn().mockReturnValue("allow"),
|
|
56
56
|
checkPermission: vi.fn().mockReturnValue({ state: "allow" }),
|
|
57
57
|
shouldUpdateActiveTools: vi.fn().mockReturnValue(true),
|
|
58
58
|
commitActiveToolsCacheKey: vi.fn(),
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { describe, expect, it, vi } from "vitest";
|
|
13
13
|
|
|
14
|
-
import { EXTENSION_TAG } from "
|
|
15
|
-
import { formatExternalDirectoryAskPrompt } from "
|
|
16
|
-
import { PermissionGateHandler } from "
|
|
14
|
+
import { EXTENSION_TAG } from "#src/denial-messages";
|
|
15
|
+
import { formatExternalDirectoryAskPrompt } from "#src/handlers/gates/external-directory-messages";
|
|
16
|
+
import { PermissionGateHandler } from "#src/handlers/permission-gate-handler";
|
|
17
17
|
import {
|
|
18
18
|
PERMISSIONS_DECISION_CHANNEL,
|
|
19
19
|
type PermissionDecisionEvent,
|
|
20
|
-
} from "
|
|
21
|
-
import type { PermissionSession } from "
|
|
22
|
-
import type { ToolRegistry } from "
|
|
23
|
-
import type { PermissionCheckResult, PermissionState } from "
|
|
20
|
+
} from "#src/permission-events";
|
|
21
|
+
import type { PermissionSession } from "#src/permission-session";
|
|
22
|
+
import type { ToolRegistry } from "#src/tool-registry";
|
|
23
|
+
import type { PermissionCheckResult, PermissionState } from "#src/types";
|
|
24
24
|
|
|
25
25
|
// ── SDK stubs ──────────────────────────────────────────────────────────────
|
|
26
26
|
vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
|
|
@@ -110,7 +110,7 @@ function makeSession(
|
|
|
110
110
|
activate: vi.fn(),
|
|
111
111
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
112
112
|
checkPermission: makeCheckPermission("deny"),
|
|
113
|
-
getToolPermission: vi.fn().mockReturnValue("allow"
|
|
113
|
+
getToolPermission: vi.fn().mockReturnValue("allow"),
|
|
114
114
|
getSessionRuleset: vi.fn().mockReturnValue([]),
|
|
115
115
|
approveSessionRule: vi.fn(),
|
|
116
116
|
getActiveSkillEntries: vi.fn().mockReturnValue([]),
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { describe, expect, it, vi } from "vitest";
|
|
13
13
|
|
|
14
|
-
import { PermissionGateHandler } from "
|
|
15
|
-
import type { PermissionSession } from "
|
|
16
|
-
import type { Rule } from "
|
|
17
|
-
import type { ToolRegistry } from "
|
|
18
|
-
import type { PermissionCheckResult } from "
|
|
19
|
-
import { wildcardMatch } from "
|
|
14
|
+
import { PermissionGateHandler } from "#src/handlers/permission-gate-handler";
|
|
15
|
+
import type { PermissionSession } from "#src/permission-session";
|
|
16
|
+
import type { Rule } from "#src/rule";
|
|
17
|
+
import type { ToolRegistry } from "#src/tool-registry";
|
|
18
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
19
|
+
import { wildcardMatch } from "#src/wildcard-matcher";
|
|
20
20
|
|
|
21
21
|
// ── SDK stub ───────────────────────────────────────────────────────────────
|
|
22
22
|
vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { describeBashExternalDirectoryGate } from "
|
|
2
|
+
import { describeBashExternalDirectoryGate } from "#src/handlers/gates/bash-external-directory";
|
|
3
3
|
import type {
|
|
4
4
|
GateBypass,
|
|
5
5
|
GateDescriptor,
|
|
6
|
-
} from "
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "../../../src/handlers/gates/descriptor";
|
|
11
|
-
import type { ToolCallContext } from "../../../src/handlers/gates/types";
|
|
12
|
-
import type { PermissionCheckResult } from "../../../src/types";
|
|
6
|
+
} from "#src/handlers/gates/descriptor";
|
|
7
|
+
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
8
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
9
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
13
10
|
|
|
14
11
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
15
12
|
|
|
@@ -9,18 +9,15 @@ vi.mock("node:os", () => {
|
|
|
9
9
|
};
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
import { describeBashPathGate } from "
|
|
12
|
+
import { describeBashPathGate } from "#src/handlers/gates/bash-path";
|
|
13
13
|
import type {
|
|
14
14
|
GateBypass,
|
|
15
15
|
GateDescriptor,
|
|
16
|
-
} from "
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from "
|
|
21
|
-
import type { ToolCallContext } from "../../../src/handlers/gates/types";
|
|
22
|
-
import type { Rule } from "../../../src/rule";
|
|
23
|
-
import type { PermissionCheckResult } from "../../../src/types";
|
|
16
|
+
} from "#src/handlers/gates/descriptor";
|
|
17
|
+
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
18
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
19
|
+
import type { Rule } from "#src/rule";
|
|
20
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
24
21
|
|
|
25
22
|
afterEach(() => {
|
|
26
23
|
vi.restoreAllMocks();
|
|
@@ -3,7 +3,7 @@ import { describe, expect, test } from "vitest";
|
|
|
3
3
|
import {
|
|
4
4
|
formatBashExternalDirectoryAskPrompt,
|
|
5
5
|
formatExternalDirectoryAskPrompt,
|
|
6
|
-
} from "
|
|
6
|
+
} from "#src/handlers/gates/external-directory-messages";
|
|
7
7
|
|
|
8
8
|
// Denial message functions (formatExternalDirectoryDenyReason,
|
|
9
9
|
// formatExternalDirectoryUserDeniedReason, formatExternalDirectoryHardStopHint,
|
|
@@ -3,13 +3,10 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import type {
|
|
4
4
|
GateBypass,
|
|
5
5
|
GateDescriptor,
|
|
6
|
-
} from "
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "../../../src/handlers/gates/descriptor";
|
|
11
|
-
import { describeExternalDirectoryGate } from "../../../src/handlers/gates/external-directory";
|
|
12
|
-
import type { ToolCallContext } from "../../../src/handlers/gates/types";
|
|
6
|
+
} from "#src/handlers/gates/descriptor";
|
|
7
|
+
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
8
|
+
import { describeExternalDirectoryGate } from "#src/handlers/gates/external-directory";
|
|
9
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
13
10
|
|
|
14
11
|
// ── helpers ───────────────────────────��────────────────────────────��───────
|
|
15
12
|
|
|
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import {
|
|
4
4
|
deriveDecisionValue,
|
|
5
5
|
deriveResolution,
|
|
6
|
-
} from "
|
|
6
|
+
} from "#src/handlers/gates/helpers";
|
|
7
7
|
|
|
8
8
|
describe("deriveDecisionValue", () => {
|
|
9
9
|
it("returns command for bash", () => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import type { GateDescriptor } from "
|
|
4
|
-
import { isGateDescriptor } from "
|
|
5
|
-
import { describePathGate } from "
|
|
6
|
-
import type { ToolCallContext } from "
|
|
7
|
-
import type { Rule } from "
|
|
8
|
-
import type { PermissionCheckResult } from "
|
|
3
|
+
import type { GateDescriptor } from "#src/handlers/gates/descriptor";
|
|
4
|
+
import { isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
5
|
+
import { describePathGate } from "#src/handlers/gates/path";
|
|
6
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
7
|
+
import type { Rule } from "#src/rule";
|
|
8
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
9
9
|
|
|
10
10
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
11
11
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import type { DenialContext } from "
|
|
4
|
-
import { EXTENSION_TAG } from "
|
|
3
|
+
import type { DenialContext } from "#src/denial-messages";
|
|
4
|
+
import { EXTENSION_TAG } from "#src/denial-messages";
|
|
5
5
|
import type {
|
|
6
6
|
GateDescriptor,
|
|
7
7
|
GateRunnerDeps,
|
|
8
|
-
} from "
|
|
9
|
-
import { runGateCheck } from "
|
|
10
|
-
import type { PermissionCheckResult } from "
|
|
8
|
+
} from "#src/handlers/gates/descriptor";
|
|
9
|
+
import { runGateCheck } from "#src/handlers/gates/runner";
|
|
10
|
+
import type { PermissionCheckResult } from "#src/types";
|
|
11
11
|
|
|
12
12
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
13
13
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import type { GateDescriptor } from "
|
|
4
|
-
import { describeSkillReadGate } from "
|
|
5
|
-
import type { ToolCallContext } from "
|
|
6
|
-
import type { SkillPromptEntry } from "
|
|
3
|
+
import type { GateDescriptor } from "#src/handlers/gates/descriptor";
|
|
4
|
+
import { describeSkillReadGate } from "#src/handlers/gates/skill-read";
|
|
5
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
6
|
+
import type { SkillPromptEntry } from "#src/skill-prompt-sanitizer";
|
|
7
7
|
|
|
8
8
|
// ── SDK stubs ──────────────────────────────────────────────────────────────
|
|
9
9
|
vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
|
|
@@ -74,7 +74,7 @@ describe("describeSkillReadGate", () => {
|
|
|
74
74
|
makeSkillEntry({ state: "ask" }),
|
|
75
75
|
]);
|
|
76
76
|
expect(result).not.toBeNull();
|
|
77
|
-
const desc = result
|
|
77
|
+
const desc = result!;
|
|
78
78
|
expect(desc.preResolved).toEqual({ state: "ask" });
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -83,7 +83,7 @@ describe("describeSkillReadGate", () => {
|
|
|
83
83
|
makeSkillEntry({ state: "allow" }),
|
|
84
84
|
]);
|
|
85
85
|
expect(result).not.toBeNull();
|
|
86
|
-
const desc = result
|
|
86
|
+
const desc = result!;
|
|
87
87
|
expect(desc.preResolved).toEqual({ state: "allow" });
|
|
88
88
|
});
|
|
89
89
|
|
|
@@ -92,14 +92,14 @@ describe("describeSkillReadGate", () => {
|
|
|
92
92
|
makeSkillEntry({ state: "deny" }),
|
|
93
93
|
]);
|
|
94
94
|
expect(result).not.toBeNull();
|
|
95
|
-
const desc = result
|
|
95
|
+
const desc = result!;
|
|
96
96
|
expect(desc.preResolved).toEqual({ state: "deny" });
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
it("decision surface is 'skill' and decision value is the skill name", () => {
|
|
100
100
|
const result = describeSkillReadGate(makeTcc(), () => [
|
|
101
101
|
makeSkillEntry({ name: "my-skill" }),
|
|
102
|
-
])
|
|
102
|
+
])!;
|
|
103
103
|
expect(result.decision.surface).toBe("skill");
|
|
104
104
|
expect(result.decision.value).toBe("my-skill");
|
|
105
105
|
});
|
|
@@ -107,7 +107,7 @@ describe("describeSkillReadGate", () => {
|
|
|
107
107
|
it("denialContext contains the skill name and read path", () => {
|
|
108
108
|
const result = describeSkillReadGate(makeTcc(), () => [
|
|
109
109
|
makeSkillEntry({ name: "librarian" }),
|
|
110
|
-
])
|
|
110
|
+
])!;
|
|
111
111
|
expect(result.denialContext).toEqual({
|
|
112
112
|
kind: "skill_read",
|
|
113
113
|
skillName: "librarian",
|
|
@@ -120,7 +120,7 @@ describe("describeSkillReadGate", () => {
|
|
|
120
120
|
const result = describeSkillReadGate(
|
|
121
121
|
makeTcc({ agentName: "test-agent", toolCallId: "tc-42" }),
|
|
122
122
|
() => [makeSkillEntry({ name: "my-skill" })],
|
|
123
|
-
)
|
|
123
|
+
)!;
|
|
124
124
|
expect(result.promptDetails).toMatchObject({
|
|
125
125
|
source: "skill_read",
|
|
126
126
|
agentName: "test-agent",
|
|
@@ -135,7 +135,7 @@ describe("describeSkillReadGate", () => {
|
|
|
135
135
|
const result = describeSkillReadGate(
|
|
136
136
|
makeTcc({ agentName: "agent-1" }),
|
|
137
137
|
() => [makeSkillEntry({ name: "librarian" })],
|
|
138
|
-
)
|
|
138
|
+
)!;
|
|
139
139
|
expect(result.logContext).toMatchObject({
|
|
140
140
|
source: "skill_read",
|
|
141
141
|
skillName: "librarian",
|
|
@@ -144,9 +144,7 @@ describe("describeSkillReadGate", () => {
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
it("surface is 'skill' on the descriptor", () => {
|
|
147
|
-
const result = describeSkillReadGate(makeTcc(), () => [
|
|
148
|
-
makeSkillEntry(),
|
|
149
|
-
]) as GateDescriptor;
|
|
147
|
+
const result = describeSkillReadGate(makeTcc(), () => [makeSkillEntry()])!;
|
|
150
148
|
expect(result.surface).toBe("skill");
|
|
151
149
|
});
|
|
152
150
|
});
|