pi-permission-system 0.4.0 → 0.4.2
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 +39 -0
- package/README.md +57 -25
- package/package.json +10 -6
- package/src/before-agent-start-cache.ts +37 -0
- package/src/index.ts +160 -56
- package/src/permission-forwarding.ts +4 -0
- package/src/permission-manager.ts +107 -22
- package/src/types-shims.d.ts +168 -166
- package/{src/config-modal-test.ts → tests/config-modal.test.ts} +3 -12
- package/{src/test.ts → tests/permission-system.test.ts} +407 -15
- package/tests/test-harness.ts +9 -0
- package/asset/pi-permission-system.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,45 @@ 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
|
+
## [0.4.2] - 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Added project-level permission layering from the active session workspace via `<cwd>/.pi/agent/pi-permissions.jsonc`
|
|
12
|
+
- Added project-level per-agent overrides via `<cwd>/.pi/agent/agents/<agent>.md` (thanks to @Talia-12 for PR #7)
|
|
13
|
+
- Added reload-aware permission manager refresh paths so policy caches are rebuilt when Pi reload events occur
|
|
14
|
+
- Added a dedicated `tests/` directory with modular test entrypoints and a shared test harness
|
|
15
|
+
- Added before-agent-start caching module to dedupe unchanged active-tool exposure and prompt state across `before_agent_start` lifecycle invocations
|
|
16
|
+
- Added `PermissionPromptDecision` type with `state` and `denialReason` fields for richer permission prompt resolution
|
|
17
|
+
- Added `getPolicyCacheStamp()` method to `PermissionManager` for cache invalidation tracking
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Global path resolution now follows Pi's `getAgentDir()` helper, so global config, agents, sessions, and logs respect `PI_CODING_AGENT_DIR` (thanks to @jvortmann for PR #6)
|
|
21
|
+
- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.67.68`
|
|
22
|
+
- Updated TypeScript project configuration and npm scripts to run tests from `tests/` instead of `src/`
|
|
23
|
+
- Updated README documentation for project-level policy files, yolo mode config, test layout, and `PI_CODING_AGENT_DIR`
|
|
24
|
+
- Permission prompts and forwarding now return `PermissionPromptDecision` instead of boolean for richer resolution tracking
|
|
25
|
+
- Permission denial messages now include user-provided denial reasons when available
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
- Removed the legacy packaged `asset/` directory because the README now uses externally hosted images instead of repository-bundled screenshots
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- `/skill:<name>` permission handling now falls back to the current merged skill policy when no active agent context is available in the main session (thanks to @NSBeidou and @hidromagnetismo for reporting the issue)
|
|
32
|
+
- Skill denial messaging now reflects whether the block came from an agent-specific rule or the merged policy without agent context
|
|
33
|
+
|
|
34
|
+
### Tests
|
|
35
|
+
- Added coverage for project-level precedence across global, project, system-agent, and project-agent layers
|
|
36
|
+
- Added coverage for resolving config from `PI_CODING_AGENT_DIR`
|
|
37
|
+
- Added coverage for before-agent-start cache key generation and state deduplication
|
|
38
|
+
- Added coverage for cache invalidation on permission policy changes
|
|
39
|
+
|
|
40
|
+
## [0.4.1] - 2026-04-01
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Updated npm keywords for improved discoverability (`pi-coding-agent`, `coding-agent`, `access-control`, `authorization`, `security`)
|
|
44
|
+
- Updated README permission prompt example image
|
|
45
|
+
- Added Related Pi Extensions cross-linking section to README
|
|
46
|
+
|
|
8
47
|
## [0.4.0] - 2026-04-01
|
|
9
48
|
|
|
10
49
|
### Added
|
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# 🔐 pi-permission-system
|
|
2
2
|
|
|
3
|
-
[](package.json)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
|
|
6
6
|
Permission enforcement extension for the Pi coding agent that provides centralized, deterministic permission gates for tool, bash, MCP, skill, and special operations.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
<img width="1360" height="752" alt="image" src="https://github.com/user-attachments/assets/3e85190a-17fa-4d94-ac8e-efa54337df5d" />
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
## Features
|
|
11
12
|
|
|
@@ -25,18 +26,20 @@ Permission enforcement extension for the Pi coding agent that provides centraliz
|
|
|
25
26
|
|
|
26
27
|
Place this folder in one of the following locations:
|
|
27
28
|
|
|
28
|
-
| Scope | Path
|
|
29
|
-
|
|
30
|
-
| Global
|
|
31
|
-
| Project | `.pi/extensions/pi-permission-system`
|
|
29
|
+
| Scope | Path |
|
|
30
|
+
|---------|------|
|
|
31
|
+
| Global default | `~/.pi/agent/extensions/pi-permission-system` (respects `PI_CODING_AGENT_DIR`) |
|
|
32
|
+
| Project | `.pi/extensions/pi-permission-system` |
|
|
32
33
|
|
|
33
34
|
Pi auto-discovers extensions in these paths.
|
|
34
35
|
|
|
36
|
+
> **Tip:** All `~/.pi/agent` paths shown in this document are defaults. If the `PI_CODING_AGENT_DIR` environment variable is set, pi uses that directory instead. The extension automatically follows pi's `getAgentDir()` helper, so global policy files, per-agent overrides, session directories, and extension installation paths all resolve under the configured agent directory.
|
|
37
|
+
|
|
35
38
|
## Usage
|
|
36
39
|
|
|
37
40
|
### Quick Start
|
|
38
41
|
|
|
39
|
-
1. Create the global policy file at `~/.pi/agent/pi-permissions.jsonc
|
|
42
|
+
1. Create the global policy file at the Pi agent runtime root (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`):
|
|
40
43
|
|
|
41
44
|
```jsonc
|
|
42
45
|
{
|
|
@@ -81,20 +84,20 @@ The extension integrates via Pi's lifecycle hooks:
|
|
|
81
84
|
- The `Available tools:` system prompt section is rewritten to match the filtered active tool set
|
|
82
85
|
- Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
|
|
83
86
|
- When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
|
|
84
|
-
- When a subagent triggers an `ask` permission without UI access, the request can be forwarded to the main session and answered there
|
|
85
87
|
|
|
86
88
|
## Configuration
|
|
87
89
|
|
|
88
90
|
### Extension Config File
|
|
89
91
|
|
|
90
|
-
**Location:** `~/.pi/agent/extensions/pi-permission-system/config.json`
|
|
92
|
+
**Location:** global Pi extension config (default: `~/.pi/agent/extensions/pi-permission-system/config.json`, respects `PI_CODING_AGENT_DIR`)
|
|
91
93
|
|
|
92
94
|
The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
|
|
93
95
|
|
|
94
96
|
```json
|
|
95
97
|
{
|
|
96
98
|
"debugLog": false,
|
|
97
|
-
"permissionReviewLog": true
|
|
99
|
+
"permissionReviewLog": true,
|
|
100
|
+
"yoloMode": false
|
|
98
101
|
}
|
|
99
102
|
```
|
|
100
103
|
|
|
@@ -102,12 +105,13 @@ The extension creates this file automatically when it is missing. It controls on
|
|
|
102
105
|
|-----|---------|-------------|
|
|
103
106
|
| `debugLog` | `false` | Enables verbose diagnostic logging to `logs/pi-permission-system-debug.jsonl` |
|
|
104
107
|
| `permissionReviewLog` | `true` | Enables the permission request/denial review log at `logs/pi-permission-system-permission-review.jsonl` |
|
|
108
|
+
| `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
|
|
105
109
|
|
|
106
110
|
Both logs write to files only under the extension directory. No debug output is printed to the terminal.
|
|
107
111
|
|
|
108
112
|
### Global Policy File
|
|
109
113
|
|
|
110
|
-
**Location:** `~/.pi/agent/pi-permissions.jsonc`
|
|
114
|
+
**Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
|
|
111
115
|
|
|
112
116
|
The policy file is a JSON object with these sections:
|
|
113
117
|
|
|
@@ -122,9 +126,9 @@ The policy file is a JSON object with these sections:
|
|
|
122
126
|
|
|
123
127
|
> **Note:** Trailing commas are **not** supported. If parsing fails, the extension falls back to `ask` for all categories.
|
|
124
128
|
|
|
125
|
-
### Per-Agent Overrides
|
|
129
|
+
### Global Per-Agent Overrides
|
|
126
130
|
|
|
127
|
-
Override global permissions for specific agents via YAML frontmatter in `~/.pi/agent/agents/<agent>.md
|
|
131
|
+
Override global permissions for specific agents via YAML frontmatter in the global Pi agents directory (default: `~/.pi/agent/agents/<agent>.md`, respects `PI_CODING_AGENT_DIR`):
|
|
128
132
|
|
|
129
133
|
```yaml
|
|
130
134
|
---
|
|
@@ -145,12 +149,29 @@ permission:
|
|
|
145
149
|
---
|
|
146
150
|
```
|
|
147
151
|
|
|
148
|
-
**Precedence:** Agent frontmatter overrides global config (shallow-merged per section).
|
|
149
|
-
|
|
150
152
|
**MCP behavior:** `permission.tools.mcp` is the coarse entry/fallback permission for a registered `mcp` tool when one is available. More specific `permission.mcp` target rules override that fallback when they match.
|
|
151
153
|
|
|
152
154
|
**Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
|
|
153
155
|
|
|
156
|
+
### Project-Level Policy Files
|
|
157
|
+
|
|
158
|
+
The extension can also layer project-local permission files relative to the active session working directory:
|
|
159
|
+
|
|
160
|
+
| Scope | Path |
|
|
161
|
+
|-------|------|
|
|
162
|
+
| Project policy | `<cwd>/.pi/agent/pi-permissions.jsonc` |
|
|
163
|
+
| Project agent override | `<cwd>/.pi/agent/agents/<agent>.md` |
|
|
164
|
+
|
|
165
|
+
Project-local files use the same formats as the global policy file and global agent frontmatter. These project files are resolved from Pi's current session `cwd`, so they are workspace-specific and do **not** move under `PI_CODING_AGENT_DIR`.
|
|
166
|
+
|
|
167
|
+
**Precedence order:**
|
|
168
|
+
1. Global policy file
|
|
169
|
+
2. Project policy file
|
|
170
|
+
3. Global agent frontmatter
|
|
171
|
+
4. Project agent frontmatter
|
|
172
|
+
|
|
173
|
+
Later layers override earlier layers within the same permission category. For wildcard-based sections like `bash`, `mcp`, `skills`, and `special`, matching still follows the extension's existing **last matching rule wins** behavior after the layers are combined.
|
|
174
|
+
|
|
154
175
|
---
|
|
155
176
|
|
|
156
177
|
## Policy Reference
|
|
@@ -255,7 +276,7 @@ A registered `mcp` tool can use `tools.mcp` as an entry permission point. This p
|
|
|
255
276
|
This is useful for per-agent configurations where you want to grant MCP access broadly:
|
|
256
277
|
|
|
257
278
|
```yaml
|
|
258
|
-
# In ~/.pi/agent/agents/researcher.md
|
|
279
|
+
# In the global Pi agents directory (default: ~/.pi/agent/agents/researcher.md; respects PI_CODING_AGENT_DIR)
|
|
259
280
|
---
|
|
260
281
|
name: researcher
|
|
261
282
|
permission:
|
|
@@ -352,7 +373,7 @@ Reserved permission checks:
|
|
|
352
373
|
|
|
353
374
|
### Per-Agent Lockdown
|
|
354
375
|
|
|
355
|
-
In `~/.pi/agent/agents/reviewer.md
|
|
376
|
+
In the global Pi agents directory (default: `~/.pi/agent/agents/reviewer.md`, respects `PI_CODING_AGENT_DIR`):
|
|
356
377
|
|
|
357
378
|
```yaml
|
|
358
379
|
---
|
|
@@ -380,7 +401,8 @@ This keeps `ask` policies usable even when the original permission check happens
|
|
|
380
401
|
When the extension prompts, denies, or forwards permission requests, it can append structured JSONL entries under:
|
|
381
402
|
|
|
382
403
|
```text
|
|
383
|
-
~/.pi/agent/extensions/pi-permission-system/logs/
|
|
404
|
+
Default global logs directory: ~/.pi/agent/extensions/pi-permission-system/logs/
|
|
405
|
+
Actual global logs directory: $PI_CODING_AGENT_DIR/extensions/pi-permission-system/logs when PI_CODING_AGENT_DIR is set
|
|
384
406
|
```
|
|
385
407
|
|
|
386
408
|
- `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history
|
|
@@ -391,16 +413,19 @@ When the extension prompts, denies, or forwards permission requests, it can appe
|
|
|
391
413
|
```
|
|
392
414
|
index.ts → Root Pi entrypoint shim
|
|
393
415
|
src/
|
|
394
|
-
├── index.ts → Extension bootstrap, permission checks, review logging, and subagent forwarding
|
|
416
|
+
├── index.ts → Extension bootstrap, permission checks, review logging, reload handling, and subagent forwarding
|
|
395
417
|
├── extension-config.ts → Extension-local config loading and default creation
|
|
396
418
|
├── logging.ts → File-only debug/review logging helpers
|
|
397
|
-
├── permission-manager.ts →
|
|
419
|
+
├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
|
|
398
420
|
├── bash-filter.ts → Bash command wildcard pattern matching
|
|
399
421
|
├── wildcard-matcher.ts → Shared wildcard pattern compilation and matching
|
|
400
422
|
├── common.ts → Shared utilities (YAML parsing, type guards, etc.)
|
|
401
423
|
├── tool-registry.ts → Registered tool name resolution
|
|
402
|
-
|
|
403
|
-
|
|
424
|
+
└── types.ts → TypeScript type definitions
|
|
425
|
+
tests/
|
|
426
|
+
├── permission-system.test.ts → Core permission, layering, forwarding, and policy tests
|
|
427
|
+
├── config-modal.test.ts → Config command and modal behavior tests
|
|
428
|
+
└── test-harness.ts → Shared lightweight test helpers
|
|
404
429
|
schemas/
|
|
405
430
|
└── permissions.schema.json → JSON Schema for policy validation
|
|
406
431
|
config/
|
|
@@ -455,10 +480,10 @@ npx --yes ajv-cli@5 validate \
|
|
|
455
480
|
|
|
456
481
|
| Problem | Cause | Solution |
|
|
457
482
|
|---------|-------|----------|
|
|
458
|
-
| Config not applied (everything asks) | File not found or parse error | Verify file
|
|
483
|
+
| Config not applied (everything asks) | File not found or parse error | Verify the global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`); check for trailing commas |
|
|
459
484
|
| Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
|
|
460
485
|
| Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
|
|
461
|
-
| `/skill:<name>` blocked |
|
|
486
|
+
| `/skill:<name>` blocked | Deny policy or confirmation unavailable | Check merged `skills` policy (global/project/agent layers). Active agent context is optional in the main session; `ask` still requires UI or forwarded confirmation. |
|
|
462
487
|
|
|
463
488
|
---
|
|
464
489
|
|
|
@@ -467,12 +492,19 @@ npx --yes ajv-cli@5 validate \
|
|
|
467
492
|
```bash
|
|
468
493
|
npm run build # Compile TypeScript
|
|
469
494
|
npm run lint # Run linter (uses build)
|
|
470
|
-
npm run test # Run tests
|
|
495
|
+
npm run test # Run tests from ./tests
|
|
471
496
|
npm run check # Run lint + test
|
|
472
497
|
```
|
|
473
498
|
|
|
474
499
|
---
|
|
475
500
|
|
|
501
|
+
## Related Pi Extensions
|
|
502
|
+
|
|
503
|
+
- [pi-multi-auth](https://github.com/MasuRii/pi-multi-auth) — Multi-provider credential management and quota-aware rotation
|
|
504
|
+
- [pi-tool-display](https://github.com/MasuRii/pi-tool-display) — Compact tool rendering and diff visualization
|
|
505
|
+
- [pi-rtk-optimizer](https://github.com/MasuRii/pi-rtk-optimizer) — RTK command rewriting and output compaction
|
|
506
|
+
- [pi-MUST-have-extension](https://github.com/MasuRii/pi-MUST-have-extension) — RFC 2119 keyword normalization for prompts
|
|
507
|
+
|
|
476
508
|
## License
|
|
477
509
|
|
|
478
510
|
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-permission-system",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Permission enforcement extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"index.ts",
|
|
12
12
|
"src",
|
|
13
|
+
"tests",
|
|
13
14
|
"config.json",
|
|
14
15
|
"config/config.example.json",
|
|
15
16
|
"schemas/permissions.schema.json",
|
|
16
|
-
"asset",
|
|
17
17
|
"README.md",
|
|
18
18
|
"CHANGELOG.md",
|
|
19
19
|
"LICENSE"
|
|
@@ -21,16 +21,20 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
|
|
23
23
|
"lint": "npm run build",
|
|
24
|
-
"test": "bun ./
|
|
24
|
+
"test": "bun ./tests/permission-system.test.ts && bun ./tests/config-modal.test.ts",
|
|
25
25
|
"check": "npm run lint && npm run test"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"pi-package",
|
|
29
29
|
"pi",
|
|
30
30
|
"pi-extension",
|
|
31
|
+
"pi-coding-agent",
|
|
32
|
+
"coding-agent",
|
|
31
33
|
"permissions",
|
|
32
34
|
"policy",
|
|
33
|
-
"
|
|
35
|
+
"access-control",
|
|
36
|
+
"authorization",
|
|
37
|
+
"security"
|
|
34
38
|
],
|
|
35
39
|
"author": "MasuRii",
|
|
36
40
|
"license": "MIT",
|
|
@@ -54,8 +58,8 @@
|
|
|
54
58
|
]
|
|
55
59
|
},
|
|
56
60
|
"peerDependencies": {
|
|
57
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
58
|
-
"@mariozechner/pi-tui": "^0.
|
|
61
|
+
"@mariozechner/pi-coding-agent": "^0.67.68",
|
|
62
|
+
"@mariozechner/pi-tui": "^0.67.68",
|
|
59
63
|
"@sinclair/typebox": "^0.34.49"
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface BeforeAgentStartPromptStateInput {
|
|
2
|
+
agentName: string | null;
|
|
3
|
+
cwd: string;
|
|
4
|
+
permissionStamp: string;
|
|
5
|
+
systemPrompt: string;
|
|
6
|
+
allowedToolNames: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeAgentName(agentName: string | null): string {
|
|
10
|
+
return agentName ?? "";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizePrompt(prompt: string): string {
|
|
14
|
+
return prompt.replace(/\r\n/g, "\n");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createCacheKey(parts: readonly unknown[]): string {
|
|
18
|
+
return JSON.stringify(parts);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createActiveToolsCacheKey(allowedToolNames: readonly string[]): string {
|
|
22
|
+
return createCacheKey(allowedToolNames);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createBeforeAgentStartPromptStateKey(input: BeforeAgentStartPromptStateInput): string {
|
|
26
|
+
return createCacheKey([
|
|
27
|
+
normalizeAgentName(input.agentName),
|
|
28
|
+
input.cwd,
|
|
29
|
+
input.permissionStamp,
|
|
30
|
+
createActiveToolsCacheKey(input.allowedToolNames),
|
|
31
|
+
normalizePrompt(input.systemPrompt),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function shouldApplyCachedAgentStartState(previousKey: string | null, nextKey: string): boolean {
|
|
36
|
+
return previousKey !== nextKey;
|
|
37
|
+
}
|