pi-permission-system 0.4.5 → 0.4.7
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 +21 -1
- package/README.md +14 -8
- package/package.json +68 -66
- package/schemas/permissions.schema.json +0 -11
- package/src/common.ts +35 -0
- package/src/config-modal.ts +223 -231
- package/src/extension-config.ts +25 -9
- package/src/index.ts +96 -83
- package/src/logging.ts +11 -9
- package/src/model-option-compatibility.ts +8 -5
- package/src/permission-forwarding.ts +29 -0
- package/src/permission-manager.ts +195 -68
- package/src/skill-prompt-sanitizer.ts +2 -34
- package/src/types-shims.d.ts +6 -0
- package/src/zellij-modal.ts +1 -1
- package/tests/config-modal.test.ts +32 -29
- package/tests/permission-system.test.ts +158 -17
- package/config.json +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,27 @@ 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
|
-
## [
|
|
8
|
+
## [0.4.7] - 2026-05-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Documented `PI_PERMISSION_SYSTEM_CONFIG_PATH` and `PI_PERMISSION_SYSTEM_LOGS_DIR` overrides for custom config and log locations.
|
|
12
|
+
- Added artifact validation to release checks for config defaults, schema support, README references, and package script safety.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Enforced trusted global/system `deny` floors so project-local policy layers can tighten permissions without relaxing higher-trust denies.
|
|
16
|
+
- Switched permission review audit entries to metadata hashes and lengths for prompts, commands, denial reasons, and tool input previews instead of raw sensitive content.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Stopped publishing `config.json` in the package so fresh installs use the runtime default with yolo mode off by default (thanks to @Nateowami for PR #18).
|
|
20
|
+
|
|
21
|
+
## [0.4.6] - 2026-04-28
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Added bounded, sanitized tool input previews to permission review logs for non-bash/non-MCP tool calls, inspired by PR #10 from @DevkumarPatel.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Reused the extension's safe JSON serialization path for generic tool approval previews so circular values and BigInts are summarized without raw full-input logging.
|
|
28
|
+
- Updated `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, and `@mariozechner/pi-tui` peer dependencies to `^0.70.5`.
|
|
9
29
|
|
|
10
30
|
## [0.4.5] - 2026-04-27
|
|
11
31
|
|
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ The extension integrates via Pi's lifecycle hooks:
|
|
|
93
93
|
- Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
|
|
94
94
|
- When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
|
|
95
95
|
- Generic extension-tool approval prompts include a bounded input preview; built-in file tools use concise human-readable summaries instead of raw multiline JSON
|
|
96
|
+
- Permission review logs include redacted prompt/input metadata for auditing without writing raw prompts, commands, or tool payload previews
|
|
96
97
|
- Path-bearing file tools (`read`, `write`, `edit`, `find`, `grep`, `ls`) evaluate `special.external_directory` before their normal tool permission when an explicit path points outside `ctx.cwd`
|
|
97
98
|
|
|
98
99
|
## Configuration
|
|
@@ -101,6 +102,8 @@ The extension integrates via Pi's lifecycle hooks:
|
|
|
101
102
|
|
|
102
103
|
**Location:** global Pi extension config (default: `~/.pi/agent/extensions/pi-permission-system/config.json`, respects `PI_CODING_AGENT_DIR`)
|
|
103
104
|
|
|
105
|
+
Set `PI_PERMISSION_SYSTEM_CONFIG_PATH` to point this extension at a specific config file when the default global path is not appropriate.
|
|
106
|
+
|
|
104
107
|
The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
|
|
105
108
|
|
|
106
109
|
```json
|
|
@@ -117,7 +120,7 @@ The extension creates this file automatically when it is missing. It controls on
|
|
|
117
120
|
| `permissionReviewLog` | `true` | Enables the permission request/denial review log at `logs/pi-permission-system-permission-review.jsonl` |
|
|
118
121
|
| `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
|
|
119
122
|
|
|
120
|
-
Both logs write to files only under the extension directory. No debug output is printed to the terminal.
|
|
123
|
+
Both logs write to files only under the extension directory by default. Set `PI_PERMISSION_SYSTEM_LOGS_DIR` to redirect review/debug logs to a specific directory. No debug output is printed to the terminal.
|
|
121
124
|
|
|
122
125
|
### Global Policy File
|
|
123
126
|
|
|
@@ -180,7 +183,7 @@ Project-local files use the same formats as the global policy file and global ag
|
|
|
180
183
|
3. Global agent frontmatter
|
|
181
184
|
4. Project agent frontmatter
|
|
182
185
|
|
|
183
|
-
Later layers override earlier layers within the same permission category. For wildcard-based sections like `bash`, `mcp`, `skills`, and `special`, matching still follows
|
|
186
|
+
Later trusted layers override earlier layers within the same permission category, and project-local layers can tighten policy by adding `deny` rules. Project-local policy cannot relax a `deny` from the global policy file or global agent frontmatter: an `allow` or `ask` in a project policy is ignored when the latest matching trusted layer is `deny`. For wildcard-based sections like `bash`, `mcp`, `skills`, and `special`, matching still follows **last matching rule wins** within the applicable trust boundary, with global/system `deny` rules acting as floors for project-local overrides.
|
|
184
187
|
|
|
185
188
|
---
|
|
186
189
|
|
|
@@ -321,7 +324,6 @@ Reserved permission checks:
|
|
|
321
324
|
|----------------------|------------------------------------------|
|
|
322
325
|
| `doom_loop` | Controls doom loop detection behavior |
|
|
323
326
|
| `external_directory` | Enforces ask/allow/deny decisions for path-bearing built-in tools (`read`, `write`, `edit`, `find`, `grep`, `ls`) when they target paths outside the active working directory |
|
|
324
|
-
| `tool_call_limit` | *(schema only, not enforced yet)* |
|
|
325
327
|
|
|
326
328
|
```jsonc
|
|
327
329
|
{
|
|
@@ -430,9 +432,10 @@ When the extension prompts, denies, or forwards permission requests, it can appe
|
|
|
430
432
|
```text
|
|
431
433
|
Default global logs directory: ~/.pi/agent/extensions/pi-permission-system/logs/
|
|
432
434
|
Actual global logs directory: $PI_CODING_AGENT_DIR/extensions/pi-permission-system/logs when PI_CODING_AGENT_DIR is set
|
|
435
|
+
Override logs directory: $PI_PERMISSION_SYSTEM_LOGS_DIR when set
|
|
433
436
|
```
|
|
434
437
|
|
|
435
|
-
- `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history
|
|
438
|
+
- `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history, including metadata hashes and lengths for prompts, commands, denial reasons, and tool input previews instead of raw sensitive content
|
|
436
439
|
- `pi-permission-system-debug.jsonl` — disabled by default and intended for troubleshooting
|
|
437
440
|
|
|
438
441
|
### Architecture
|
|
@@ -521,11 +524,14 @@ npx --yes ajv-cli@5 validate \
|
|
|
521
524
|
|
|
522
525
|
## Development
|
|
523
526
|
|
|
527
|
+
Runtime checks require Node.js 20+; the test suite requires Bun 1.1+.
|
|
528
|
+
|
|
524
529
|
```bash
|
|
525
|
-
npm run build
|
|
526
|
-
npm run lint
|
|
527
|
-
npm run
|
|
528
|
-
npm run
|
|
530
|
+
npm run build # Run TypeScript type checks
|
|
531
|
+
npm run lint # Run local static checks
|
|
532
|
+
npm run validate:artifacts # Validate JSON/schema/example artifacts
|
|
533
|
+
npm run test # Run Bun tests from ./tests
|
|
534
|
+
npm run check # Run static, artifact, and test checks
|
|
529
535
|
```
|
|
530
536
|
|
|
531
537
|
---
|
package/package.json
CHANGED
|
@@ -1,66 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-permission-system",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Permission enforcement extension for the Pi coding agent.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./index.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./index.ts"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"index.ts",
|
|
12
|
-
"src",
|
|
13
|
-
"tests",
|
|
14
|
-
"config.json",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"build": "
|
|
23
|
-
"lint": "npm run
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"pi",
|
|
30
|
-
"pi
|
|
31
|
-
"pi-
|
|
32
|
-
"coding-agent",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"@mariozechner/pi-
|
|
64
|
-
"@
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-permission-system",
|
|
3
|
+
"version": "0.4.7",
|
|
4
|
+
"description": "Permission enforcement extension for the Pi coding agent.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.ts",
|
|
12
|
+
"src",
|
|
13
|
+
"tests",
|
|
14
|
+
"config/config.example.json",
|
|
15
|
+
"schemas/permissions.schema.json",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"typecheck": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noEmit",
|
|
22
|
+
"build": "npm run typecheck",
|
|
23
|
+
"lint": "npm run typecheck",
|
|
24
|
+
"validate:artifacts": "node ./scripts/validate-artifacts.mjs",
|
|
25
|
+
"test": "bun ./tests/permission-system.test.ts && bun ./tests/config-modal.test.ts",
|
|
26
|
+
"check": "npm run lint && npm run validate:artifacts && npm run test"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"pi-package",
|
|
30
|
+
"pi",
|
|
31
|
+
"pi-extension",
|
|
32
|
+
"pi-coding-agent",
|
|
33
|
+
"coding-agent",
|
|
34
|
+
"permissions",
|
|
35
|
+
"policy",
|
|
36
|
+
"access-control",
|
|
37
|
+
"authorization",
|
|
38
|
+
"security"
|
|
39
|
+
],
|
|
40
|
+
"author": "MasuRii",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/MasuRii/pi-permission-system.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/MasuRii/pi-permission-system#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/MasuRii/pi-permission-system/issues"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20",
|
|
52
|
+
"bun": ">=1.1.0"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"pi": {
|
|
58
|
+
"extensions": [
|
|
59
|
+
"./index.ts"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@mariozechner/pi-ai": "^0.72.0",
|
|
64
|
+
"@mariozechner/pi-coding-agent": "^0.72.0",
|
|
65
|
+
"@mariozechner/pi-tui": "^0.72.0",
|
|
66
|
+
"@sinclair/typebox": "^0.34.49"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/common.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, normalize, resolve, sep } from "node:path";
|
|
3
|
+
|
|
1
4
|
import type { PermissionState } from "./types.js";
|
|
2
5
|
|
|
3
6
|
export function toRecord(value: unknown): Record<string, unknown> {
|
|
@@ -21,6 +24,38 @@ export function isPermissionState(value: unknown): value is PermissionState {
|
|
|
21
24
|
return value === "allow" || value === "deny" || value === "ask";
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
export function normalizePathForComparison(pathValue: string, cwd: string): string {
|
|
28
|
+
const trimmed = pathValue.trim().replace(/^["']|["']$/g, "");
|
|
29
|
+
if (!trimmed) {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let normalizedPath = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
34
|
+
|
|
35
|
+
if (normalizedPath === "~") {
|
|
36
|
+
normalizedPath = homedir();
|
|
37
|
+
} else if (normalizedPath.startsWith("~/") || normalizedPath.startsWith("~\\")) {
|
|
38
|
+
normalizedPath = join(homedir(), normalizedPath.slice(2));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const absolutePath = resolve(cwd, normalizedPath);
|
|
42
|
+
const normalizedAbsolutePath = normalize(absolutePath);
|
|
43
|
+
return process.platform === "win32" ? normalizedAbsolutePath.toLowerCase() : normalizedAbsolutePath;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function isPathWithinDirectory(pathValue: string, directory: string): boolean {
|
|
47
|
+
if (!pathValue || !directory) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (pathValue === directory) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const prefix = directory.endsWith(sep) ? directory : `${directory}${sep}`;
|
|
56
|
+
return pathValue.startsWith(prefix);
|
|
57
|
+
}
|
|
58
|
+
|
|
24
59
|
type StackNode = { indent: number; target: Record<string, unknown> };
|
|
25
60
|
|
|
26
61
|
export function parseSimpleYamlMap(input: string): Record<string, unknown> {
|