@gotgenes/pi-permission-system 18.1.1 → 18.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 +25 -0
- package/docs/configuration.md +11 -0
- package/package.json +1 -2
- package/src/access-intent/access-path.ts +29 -4
- package/src/access-intent/bash/bash-path-resolver.ts +39 -18
- package/src/access-intent/bash/msys-bash-tokens.ts +64 -0
- package/src/extension-config.ts +7 -0
- package/src/forwarded-permissions/permission-forwarder.ts +6 -3
- package/src/handlers/gates/helpers.ts +1 -1
- package/src/handlers/gates/runner.ts +22 -0
- package/src/index.ts +10 -6
- package/src/path-normalizer.ts +105 -2
- package/src/permission-manager.ts +21 -1
- package/src/permission-prompter.ts +10 -16
- package/src/prompting-gateway.ts +13 -17
- package/src/rule.ts +21 -1
- package/src/status.ts +1 -1
- package/src/yolo-mode.ts +0 -30
- package/test/access-intent/access-path.test.ts +0 -277
- package/test/access-intent/bash/node-text.test.ts +0 -148
- package/test/access-intent/bash/parser.test.ts +0 -19
- package/test/access-intent/bash/program.test.ts +0 -673
- package/test/access-intent/bash/token-classification.test.ts +0 -363
- package/test/access-intent/bash/token-collection.test.ts +0 -300
- package/test/active-agent.test.ts +0 -155
- package/test/async-cache.test.ts +0 -48
- package/test/bash-arity.test.ts +0 -144
- package/test/bash-external-directory.test.ts +0 -1022
- package/test/builtin-tool-input-formatters.test.ts +0 -109
- package/test/canonicalize-path.test.ts +0 -119
- package/test/composition-root.test.ts +0 -698
- package/test/config-loader.test.ts +0 -740
- package/test/config-modal.test.ts +0 -320
- package/test/config-paths.test.ts +0 -83
- package/test/config-pipeline.test.ts +0 -90
- package/test/config-reporter.test.ts +0 -147
- package/test/config-store.test.ts +0 -466
- package/test/decision-audit.test.ts +0 -72
- package/test/decision-reporter.test.ts +0 -112
- package/test/denial-messages.test.ts +0 -714
- package/test/detect-permissive-bash-fallback.test.ts +0 -56
- package/test/expand-home.test.ts +0 -93
- package/test/extension-config.test.ts +0 -129
- package/test/extension-paths.test.ts +0 -108
- package/test/forwarded-permissions/io.test.ts +0 -251
- package/test/forwarding-manager.test.ts +0 -200
- package/test/handlers/before-agent-start.test.ts +0 -314
- package/test/handlers/external-directory-integration.test.ts +0 -515
- package/test/handlers/external-directory-session-dedup.test.ts +0 -175
- package/test/handlers/external-directory-symlink-acceptance.test.ts +0 -167
- package/test/handlers/gates/bash-command-metamorphic.test.ts +0 -88
- package/test/handlers/gates/bash-command.test.ts +0 -257
- package/test/handlers/gates/bash-external-directory.test.ts +0 -268
- package/test/handlers/gates/bash-path.test.ts +0 -346
- package/test/handlers/gates/candidate-check.test.ts +0 -52
- package/test/handlers/gates/external-directory-messages.test.ts +0 -85
- package/test/handlers/gates/external-directory-policy.test.ts +0 -134
- package/test/handlers/gates/external-directory.test.ts +0 -267
- package/test/handlers/gates/helpers.test.ts +0 -165
- package/test/handlers/gates/path.test.ts +0 -369
- package/test/handlers/gates/runner.test.ts +0 -408
- package/test/handlers/gates/skill-input-gate-pipeline.test.ts +0 -176
- package/test/handlers/gates/skill-input.test.ts +0 -128
- package/test/handlers/gates/skill-read.test.ts +0 -161
- package/test/handlers/gates/tool-call-gate-pipeline.test.ts +0 -356
- package/test/handlers/gates/tool.test.ts +0 -244
- package/test/handlers/input-events.test.ts +0 -168
- package/test/handlers/input.test.ts +0 -199
- package/test/handlers/lifecycle.test.ts +0 -221
- package/test/handlers/tool-call-boundary.test.ts +0 -145
- package/test/handlers/tool-call-events.test.ts +0 -277
- package/test/handlers/tool-call.test.ts +0 -395
- package/test/handlers/validate-requested-tool.test.ts +0 -92
- package/test/helpers/external-directory-fixtures.ts +0 -269
- package/test/helpers/gate-fixtures.ts +0 -316
- package/test/helpers/handler-fixtures.ts +0 -335
- package/test/helpers/make-fake-pi.ts +0 -100
- package/test/helpers/manager-harness.ts +0 -112
- package/test/helpers/session-fixtures.ts +0 -199
- package/test/input-normalizer.test.ts +0 -325
- package/test/logging.test.ts +0 -51
- package/test/mcp-targets.test.ts +0 -233
- package/test/node-modules-discovery.test.ts +0 -97
- package/test/normalize.test.ts +0 -247
- package/test/path-containment.test.ts +0 -161
- package/test/path-normalization.test.ts +0 -233
- package/test/path-normalizer.test.ts +0 -196
- package/test/path-surfaces.test.ts +0 -55
- package/test/pattern-suggest.test.ts +0 -248
- package/test/permission-dialog.test.ts +0 -205
- package/test/permission-event-rpc.test.ts +0 -560
- package/test/permission-events.test.ts +0 -400
- package/test/permission-forwarder.test.ts +0 -370
- package/test/permission-forwarding.test.ts +0 -315
- package/test/permission-gate.test.ts +0 -269
- package/test/permission-manager-unified.test.ts +0 -3714
- package/test/permission-merge.test.ts +0 -61
- package/test/permission-prompter.test.ts +0 -518
- package/test/permission-prompts.test.ts +0 -363
- package/test/permission-resolver.test.ts +0 -277
- package/test/permission-session.test.ts +0 -404
- package/test/permission-ui-prompt.test.ts +0 -146
- package/test/permissions-service.test.ts +0 -192
- package/test/pi-infrastructure-read.test.ts +0 -432
- package/test/policy-loader.test.ts +0 -561
- package/test/prompting-gateway.test.ts +0 -231
- package/test/rule.test.ts +0 -650
- package/test/safe-system-paths.test.ts +0 -46
- package/test/scope-merge.test.ts +0 -116
- package/test/service-lifecycle.test.ts +0 -163
- package/test/service.test.ts +0 -261
- package/test/session-approval.test.ts +0 -75
- package/test/session-logger.test.ts +0 -200
- package/test/session-rules.test.ts +0 -321
- package/test/session-start.test.ts +0 -112
- package/test/skill-prompt-sanitizer.test.ts +0 -418
- package/test/status.test.ts +0 -10
- package/test/subagent-context.test.ts +0 -372
- package/test/subagent-lifecycle-events.test.ts +0 -132
- package/test/subagent-registry.test.ts +0 -145
- package/test/synthesize.test.ts +0 -302
- package/test/system-prompt-sanitizer.test.ts +0 -382
- package/test/tool-access-extractor-registry.test.ts +0 -77
- package/test/tool-input-formatter-registry.test.ts +0 -75
- package/test/tool-input-path.test.ts +0 -84
- package/test/tool-input-preview.test.ts +0 -129
- package/test/tool-input-prompt-formatters.test.ts +0 -115
- package/test/tool-preview-formatter.test.ts +0 -458
- package/test/tool-registry.test.ts +0 -197
- package/test/value-guards.test.ts +0 -193
- package/test/wildcard-matcher.test.ts +0 -424
- package/test/yaml-frontmatter.test.ts +0 -91
- package/test/yolo-mode.test.ts +0 -188
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
formatMcpInputForPrompt,
|
|
5
|
-
registerBuiltinToolInputFormatters,
|
|
6
|
-
} from "#src/builtin-tool-input-formatters";
|
|
7
|
-
import { ToolInputFormatterRegistry } from "#src/tool-input-formatter-registry";
|
|
8
|
-
|
|
9
|
-
// ── formatMcpInputForPrompt ───────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
describe("formatMcpInputForPrompt", () => {
|
|
12
|
-
test("returns undefined when arguments is absent", () => {
|
|
13
|
-
expect(formatMcpInputForPrompt({ tool: "exa:search" })).toBeUndefined();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("returns undefined when arguments is an empty object", () => {
|
|
17
|
-
expect(
|
|
18
|
-
formatMcpInputForPrompt({ tool: "exa:search", arguments: {} }),
|
|
19
|
-
).toBeUndefined();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("returns a summary for a single string argument", () => {
|
|
23
|
-
const result = formatMcpInputForPrompt({
|
|
24
|
-
tool: "exa:search",
|
|
25
|
-
arguments: { query: "typescript generics" },
|
|
26
|
-
});
|
|
27
|
-
expect(result).toBeDefined();
|
|
28
|
-
expect(result).toContain("query");
|
|
29
|
-
expect(result).toContain("typescript generics");
|
|
30
|
-
expect(result).toMatch(/^with /);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("returns a comma-separated summary for multiple arguments", () => {
|
|
34
|
-
const result = formatMcpInputForPrompt({
|
|
35
|
-
tool: "exa:search",
|
|
36
|
-
arguments: { query: "test", numResults: 5 },
|
|
37
|
-
});
|
|
38
|
-
expect(result).toContain("query");
|
|
39
|
-
expect(result).toContain("numResults");
|
|
40
|
-
expect(result).toContain("5");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("renders number arguments without quotes", () => {
|
|
44
|
-
const result = formatMcpInputForPrompt({
|
|
45
|
-
arguments: { count: 42 },
|
|
46
|
-
});
|
|
47
|
-
expect(result).toContain("42");
|
|
48
|
-
expect(result).not.toContain('"42"');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("renders boolean arguments without quotes", () => {
|
|
52
|
-
const result = formatMcpInputForPrompt({
|
|
53
|
-
arguments: { verbose: true },
|
|
54
|
-
});
|
|
55
|
-
expect(result).toContain("true");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("renders array arguments as '[N items]'", () => {
|
|
59
|
-
const result = formatMcpInputForPrompt({
|
|
60
|
-
arguments: { ids: [1, 2, 3] },
|
|
61
|
-
});
|
|
62
|
-
expect(result).toContain("[3 items]");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("renders nested object arguments as '{…}'", () => {
|
|
66
|
-
const result = formatMcpInputForPrompt({
|
|
67
|
-
arguments: { filter: { type: "file" } },
|
|
68
|
-
});
|
|
69
|
-
expect(result).toContain("{…}");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("truncates the full summary when it exceeds the limit", () => {
|
|
73
|
-
// Need multiple long-valued args so the joined summary exceeds 160 chars
|
|
74
|
-
const result = formatMcpInputForPrompt({
|
|
75
|
-
arguments: {
|
|
76
|
-
first: "x".repeat(80),
|
|
77
|
-
second: "y".repeat(80),
|
|
78
|
-
third: "z".repeat(80),
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
expect(result).toBeDefined();
|
|
82
|
-
expect(result!.endsWith("…")).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("truncates long string argument values", () => {
|
|
86
|
-
const result = formatMcpInputForPrompt({
|
|
87
|
-
arguments: { query: "x".repeat(100) },
|
|
88
|
-
});
|
|
89
|
-
expect(result).toBeDefined();
|
|
90
|
-
// Should not include the full 100-char string verbatim
|
|
91
|
-
expect(result).not.toContain("x".repeat(100));
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// ── registerBuiltinToolInputFormatters ────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
describe("registerBuiltinToolInputFormatters", () => {
|
|
98
|
-
test("registers the mcp formatter in the registry", () => {
|
|
99
|
-
const registry = new ToolInputFormatterRegistry();
|
|
100
|
-
registerBuiltinToolInputFormatters(registry);
|
|
101
|
-
expect(registry.get("mcp")).toBe(formatMcpInputForPrompt);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("throws if called twice (duplicate registration guard)", () => {
|
|
105
|
-
const registry = new ToolInputFormatterRegistry();
|
|
106
|
-
registerBuiltinToolInputFormatters(registry);
|
|
107
|
-
expect(() => registerBuiltinToolInputFormatters(registry)).toThrow("mcp");
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const realpathSync = vi.hoisted(() => vi.fn<(path: string) => string>());
|
|
4
|
-
|
|
5
|
-
vi.mock("node:fs", () => ({
|
|
6
|
-
realpathSync,
|
|
7
|
-
default: { realpathSync },
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
import { canonicalizePath } from "#src/canonicalize-path";
|
|
11
|
-
|
|
12
|
-
function enoent(p: string): NodeJS.ErrnoException {
|
|
13
|
-
return Object.assign(new Error(`ENOENT: no such file or directory '${p}'`), {
|
|
14
|
-
code: "ENOENT",
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe("canonicalizePath", () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
realpathSync.mockReset();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("returns empty string for empty input", () => {
|
|
24
|
-
expect(canonicalizePath("", "linux")).toBe("");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("returns realpathSync result when path exists", () => {
|
|
28
|
-
realpathSync.mockReturnValueOnce("/real/projects/app");
|
|
29
|
-
expect(canonicalizePath("/projects/link", "linux")).toBe(
|
|
30
|
-
"/real/projects/app",
|
|
31
|
-
);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("re-appends a non-existent leaf to the canonical parent", () => {
|
|
35
|
-
realpathSync
|
|
36
|
-
.mockImplementationOnce(() => {
|
|
37
|
-
throw enoent("/projects/app/new-file.ts");
|
|
38
|
-
})
|
|
39
|
-
.mockReturnValueOnce("/canonical/app");
|
|
40
|
-
expect(canonicalizePath("/projects/app/new-file.ts", "linux")).toBe(
|
|
41
|
-
"/canonical/app/new-file.ts",
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("walks up multiple levels for a deeply non-existent path", () => {
|
|
46
|
-
realpathSync
|
|
47
|
-
.mockImplementationOnce(() => {
|
|
48
|
-
throw enoent("/projects/app/src/new-file.ts");
|
|
49
|
-
})
|
|
50
|
-
.mockImplementationOnce(() => {
|
|
51
|
-
throw enoent("/projects/app/src");
|
|
52
|
-
})
|
|
53
|
-
.mockImplementationOnce(() => {
|
|
54
|
-
throw enoent("/projects/app");
|
|
55
|
-
})
|
|
56
|
-
.mockReturnValueOnce("/canonical/projects");
|
|
57
|
-
expect(canonicalizePath("/projects/app/src/new-file.ts", "linux")).toBe(
|
|
58
|
-
"/canonical/projects/app/src/new-file.ts",
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("returns input unchanged when walk reaches filesystem root (all ENOENT)", () => {
|
|
63
|
-
realpathSync.mockImplementation(() => {
|
|
64
|
-
throw enoent("");
|
|
65
|
-
});
|
|
66
|
-
expect(canonicalizePath("/nonexistent/path/file.ts", "linux")).toBe(
|
|
67
|
-
"/nonexistent/path/file.ts",
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test("returns input unchanged on ELOOP (symlink loop)", () => {
|
|
72
|
-
realpathSync.mockImplementation(() => {
|
|
73
|
-
throw Object.assign(new Error("ELOOP"), { code: "ELOOP" });
|
|
74
|
-
});
|
|
75
|
-
expect(canonicalizePath("/some/looping/path", "linux")).toBe(
|
|
76
|
-
"/some/looping/path",
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("returns input unchanged on EACCES (permission denied)", () => {
|
|
81
|
-
realpathSync.mockImplementation(() => {
|
|
82
|
-
throw Object.assign(new Error("EACCES"), { code: "EACCES" });
|
|
83
|
-
});
|
|
84
|
-
expect(canonicalizePath("/restricted/path", "linux")).toBe(
|
|
85
|
-
"/restricted/path",
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("handles ENOTDIR by walking up (like ENOENT)", () => {
|
|
90
|
-
realpathSync
|
|
91
|
-
.mockImplementationOnce(() => {
|
|
92
|
-
throw Object.assign(new Error("ENOTDIR"), { code: "ENOTDIR" });
|
|
93
|
-
})
|
|
94
|
-
.mockReturnValueOnce("/real/parent");
|
|
95
|
-
expect(canonicalizePath("/real/parent/not-a-dir", "linux")).toBe(
|
|
96
|
-
"/real/parent/not-a-dir",
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// ── injected platform flavor (win32-separator splitting) ──────────────
|
|
101
|
-
|
|
102
|
-
test("win32: splits and rejoins on the backslash separator", () => {
|
|
103
|
-
realpathSync
|
|
104
|
-
.mockImplementationOnce(() => {
|
|
105
|
-
throw enoent("C:\\projects\\link\\file.ts");
|
|
106
|
-
})
|
|
107
|
-
.mockReturnValueOnce("C:\\real\\app");
|
|
108
|
-
expect(canonicalizePath("C:\\projects\\link\\file.ts", "win32")).toBe(
|
|
109
|
-
"C:\\real\\app\\file.ts",
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("win32: resolves an existing path via realpathSync", () => {
|
|
114
|
-
realpathSync.mockReturnValueOnce("C:\\real\\app");
|
|
115
|
-
expect(canonicalizePath("C:\\projects\\link", "win32")).toBe(
|
|
116
|
-
"C:\\real\\app",
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
});
|