@gotgenes/pi-permission-system 0.7.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 +384 -0
- package/LICENSE +21 -0
- package/README.md +606 -0
- package/config/config.example.json +27 -0
- package/index.ts +3 -0
- package/package.json +85 -0
- package/schemas/permissions.schema.json +88 -0
- package/src/bash-filter.ts +51 -0
- package/src/before-agent-start-cache.ts +44 -0
- package/src/common.ts +88 -0
- package/src/config-modal.ts +282 -0
- package/src/config-reporter.ts +26 -0
- package/src/extension-config.ts +203 -0
- package/src/index.ts +1983 -0
- package/src/logging.ts +118 -0
- package/src/model-option-compatibility.ts +182 -0
- package/src/permission-dialog.ts +89 -0
- package/src/permission-forwarding.ts +126 -0
- package/src/permission-manager.ts +989 -0
- package/src/skill-prompt-sanitizer.ts +344 -0
- package/src/status.ts +35 -0
- package/src/system-prompt-sanitizer.ts +210 -0
- package/src/tool-registry.ts +139 -0
- package/src/types.ts +50 -0
- package/src/wildcard-matcher.ts +84 -0
- package/src/yolo-mode.ts +29 -0
- package/src/zellij-modal.ts +1117 -0
- package/tests/config-modal.test.ts +248 -0
- package/tests/config-reporter.test.ts +139 -0
- package/tests/extension-config.test.ts +120 -0
- package/tests/permission-system.test.ts +2356 -0
- package/tests/session-start.test.ts +139 -0
package/README.md
ADDED
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
# 🔐 @gotgenes/pi-permission-system
|
|
2
|
+
|
|
3
|
+
[](package.json)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Permission enforcement extension for the Pi coding agent that provides centralized, deterministic permission gates for tool, bash, MCP, skill, and special operations.
|
|
7
|
+
|
|
8
|
+
> **Fork notice:** This package is a friendly fork of [MasuRii/pi-permission-system](https://github.com/MasuRii/pi-permission-system), published to npm as `@gotgenes/pi-permission-system`. The extension's on-disk identity (config directory, log filenames, `/permission-system` slash command, and event channel names) is intentionally preserved so this fork and upstream share runtime state and remain drop-in interchangeable.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Tool Filtering** — Hides disallowed tools from the agent before it starts (reduces "try another tool" behavior)
|
|
13
|
+
- **System Prompt Sanitization** — Removes denied tool entries from the `Available tools:` system prompt section so the agent only sees tools it can actually call
|
|
14
|
+
- **Runtime Enforcement** — Blocks/asks/allows at tool call time with UI confirmation dialogs and readable approval summaries
|
|
15
|
+
- **Bash Command Control** — Wildcard pattern matching for granular bash command permissions
|
|
16
|
+
- **MCP Access Control** — Server and tool-level permissions for MCP operations
|
|
17
|
+
- **Skill Protection** — Controls which skills can be loaded or read from disk, including multi-block prompt sanitization
|
|
18
|
+
- **Per-Agent Overrides** — Agent-specific permission policies via YAML frontmatter
|
|
19
|
+
- **Subagent Permission Forwarding** — Forwards `ask` confirmations from non-UI subagents back to the main interactive session
|
|
20
|
+
- **File-Based Review Logging** — Writes permission request/denial review entries to a file by default for later auditing
|
|
21
|
+
- **Optional Debug Logging** — Keeps verbose extension diagnostics in a separate file when enabled in `config.json`
|
|
22
|
+
- **JSON Schema Validation** — Full schema for editor autocomplete and config validation
|
|
23
|
+
- **External Directory Guard** — Enforces `special.external_directory` for path-bearing file tools that target paths outside the active working directory
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### npm package
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pi install npm:pi-permission-system
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Local extension folder
|
|
34
|
+
|
|
35
|
+
Place this folder in one of the following locations:
|
|
36
|
+
|
|
37
|
+
| Scope | Path |
|
|
38
|
+
| -------------- | ------------------------------------------------------------------------------ |
|
|
39
|
+
| Global default | `~/.pi/agent/extensions/pi-permission-system` (respects `PI_CODING_AGENT_DIR`) |
|
|
40
|
+
| Project | `.pi/extensions/pi-permission-system` |
|
|
41
|
+
|
|
42
|
+
Pi auto-discovers extensions in these paths.
|
|
43
|
+
|
|
44
|
+
> **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.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Quick Start
|
|
49
|
+
|
|
50
|
+
1. Create the global policy file at the Pi agent runtime root (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`):
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
{
|
|
54
|
+
"defaultPolicy": {
|
|
55
|
+
"tools": "ask",
|
|
56
|
+
"bash": "ask",
|
|
57
|
+
"mcp": "ask",
|
|
58
|
+
"skills": "ask",
|
|
59
|
+
"special": "ask",
|
|
60
|
+
},
|
|
61
|
+
"tools": {
|
|
62
|
+
"read": "allow",
|
|
63
|
+
"write": "deny",
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
1. Start Pi — the extension automatically loads and enforces your policy.
|
|
69
|
+
|
|
70
|
+
### Permission States
|
|
71
|
+
|
|
72
|
+
All permissions use one of three states:
|
|
73
|
+
|
|
74
|
+
| State | Behavior |
|
|
75
|
+
| ------- | ---------------------------------------- |
|
|
76
|
+
| `allow` | Permits the action silently |
|
|
77
|
+
| `deny` | Blocks the action with an error message |
|
|
78
|
+
| `ask` | Prompts the user for confirmation via UI |
|
|
79
|
+
|
|
80
|
+
### Pi Integration Hooks
|
|
81
|
+
|
|
82
|
+
The extension integrates via Pi's lifecycle hooks:
|
|
83
|
+
|
|
84
|
+
| Hook | Behavior |
|
|
85
|
+
| -------------------- | ------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `before_agent_start` | Filters active tools, removes denied tool entries from the system prompt, and hides denied skills |
|
|
87
|
+
| `tool_call` | Enforces permissions for every tool invocation |
|
|
88
|
+
| `input` | Intercepts `/skill:<name>` requests and enforces skill policy |
|
|
89
|
+
|
|
90
|
+
**Additional behaviors:**
|
|
91
|
+
|
|
92
|
+
- Unknown/unregistered tools are blocked before permission checks (prevents bypass attempts)
|
|
93
|
+
- The `Available tools:` system prompt section is rewritten to match the filtered active tool set
|
|
94
|
+
- Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
|
|
95
|
+
- When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
|
|
96
|
+
- Generic extension-tool approval prompts include a bounded input preview; built-in file tools use concise human-readable summaries instead of raw multiline JSON
|
|
97
|
+
- Permission review logs include bounded `toolInputPreview` values for non-bash/non-MCP tool calls so approvals can be audited without writing raw full payloads
|
|
98
|
+
- 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`
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
### Extension Config File
|
|
103
|
+
|
|
104
|
+
**Location:** global Pi extension config (default: `~/.pi/agent/extensions/pi-permission-system/config.json`, respects `PI_CODING_AGENT_DIR`)
|
|
105
|
+
|
|
106
|
+
The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"debugLog": false,
|
|
111
|
+
"permissionReviewLog": true,
|
|
112
|
+
"yoloMode": false
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| Key | Default | Description |
|
|
117
|
+
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `debugLog` | `false` | Enables verbose diagnostic logging to `logs/pi-permission-system-debug.jsonl` |
|
|
119
|
+
| `permissionReviewLog` | `true` | Enables the permission request/denial review log at `logs/pi-permission-system-permission-review.jsonl` |
|
|
120
|
+
| `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
|
|
121
|
+
|
|
122
|
+
Both logs write to files only under the extension directory. No debug output is printed to the terminal.
|
|
123
|
+
|
|
124
|
+
> **Note:** Permission-rule keys (`defaultPolicy`, `tools`, `bash`, `mcp`, `skills`, `special`, `external_directory`, `doom_loop`) placed in `config.json` are silently ignored — they belong in the policy file below.
|
|
125
|
+
> The extension warns at startup when it detects misplaced keys.
|
|
126
|
+
|
|
127
|
+
### Global Policy File
|
|
128
|
+
|
|
129
|
+
**Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
|
|
130
|
+
|
|
131
|
+
The policy file is a JSON object with these sections:
|
|
132
|
+
|
|
133
|
+
| Section | Description |
|
|
134
|
+
| --------------- | ---------------------------------------------------------------------------- |
|
|
135
|
+
| `defaultPolicy` | Fallback permissions per category |
|
|
136
|
+
| `tools` | Exact-name tool permissions for registered tools |
|
|
137
|
+
| `bash` | Command pattern permissions |
|
|
138
|
+
| `mcp` | MCP server/tool permissions for calls routed through a registered `mcp` tool |
|
|
139
|
+
| `skills` | Skill name pattern permissions |
|
|
140
|
+
| `special` | Reserved permission checks such as external directory access |
|
|
141
|
+
|
|
142
|
+
> **Note:** Trailing commas are **not** supported. If parsing fails, the extension falls back to `ask` for all categories.
|
|
143
|
+
|
|
144
|
+
### Global Per-Agent Overrides
|
|
145
|
+
|
|
146
|
+
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`):
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
---
|
|
150
|
+
name: my-agent
|
|
151
|
+
permission:
|
|
152
|
+
tools:
|
|
153
|
+
read: allow
|
|
154
|
+
write: deny
|
|
155
|
+
mcp: allow
|
|
156
|
+
bash:
|
|
157
|
+
git status: allow
|
|
158
|
+
git *: ask
|
|
159
|
+
mcp:
|
|
160
|
+
chrome_devtools_*: deny
|
|
161
|
+
exa_*: allow
|
|
162
|
+
skills:
|
|
163
|
+
"*": ask
|
|
164
|
+
---
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**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.
|
|
168
|
+
|
|
169
|
+
**Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
|
|
170
|
+
|
|
171
|
+
### Project-Level Policy Files
|
|
172
|
+
|
|
173
|
+
The extension can also layer project-local permission files relative to the active session working directory:
|
|
174
|
+
|
|
175
|
+
| Scope | Path |
|
|
176
|
+
| ---------------------- | -------------------------------------- |
|
|
177
|
+
| Project policy | `<cwd>/.pi/agent/pi-permissions.jsonc` |
|
|
178
|
+
| Project agent override | `<cwd>/.pi/agent/agents/<agent>.md` |
|
|
179
|
+
|
|
180
|
+
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`.
|
|
181
|
+
|
|
182
|
+
**Precedence order:**
|
|
183
|
+
|
|
184
|
+
1. Global policy file
|
|
185
|
+
2. Project policy file
|
|
186
|
+
3. Global agent frontmatter
|
|
187
|
+
4. Project agent frontmatter
|
|
188
|
+
|
|
189
|
+
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. The recommended convention — also used by [OpenCode's permission model](https://opencode.ai/docs/permissions/#granular-rules-object-syntax) — is to put the broad catch-all rule first and specific overrides after it.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Policy Reference
|
|
194
|
+
|
|
195
|
+
### `defaultPolicy`
|
|
196
|
+
|
|
197
|
+
Sets fallback permissions when no specific rule matches:
|
|
198
|
+
|
|
199
|
+
```jsonc
|
|
200
|
+
{
|
|
201
|
+
"defaultPolicy": {
|
|
202
|
+
"tools": "ask",
|
|
203
|
+
"bash": "ask",
|
|
204
|
+
"mcp": "ask",
|
|
205
|
+
"skills": "ask",
|
|
206
|
+
"special": "ask",
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `tools`
|
|
212
|
+
|
|
213
|
+
Controls tools by exact registered name (no wildcards). This is the recommended standalone format for **all** tool entries, including Pi built-ins and arbitrary third-party extension tools.
|
|
214
|
+
|
|
215
|
+
| Tool name example | Description |
|
|
216
|
+
| ------------------ | ------------------------------------------------------------------------- |
|
|
217
|
+
| `bash` | Shell command execution (tool-level fallback before `bash` pattern rules) |
|
|
218
|
+
| `read` / `write` | Canonical Pi built-in file tools |
|
|
219
|
+
| `mcp` | Registered MCP proxy tool entry/fallback when available |
|
|
220
|
+
| `task` | Delegation tool handled like any other registered extension tool |
|
|
221
|
+
| `third_party_tool` | Arbitrary registered extension tool |
|
|
222
|
+
|
|
223
|
+
```jsonc
|
|
224
|
+
{
|
|
225
|
+
"tools": {
|
|
226
|
+
"read": "allow",
|
|
227
|
+
"write": "deny",
|
|
228
|
+
"mcp": "allow",
|
|
229
|
+
"third_party_tool": "ask",
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Unknown or absent tools are not required in the config. If another extension is not installed, its tool simply will not be registered at runtime, and this extension will block attempts to call that missing tool before permission checks run.
|
|
235
|
+
|
|
236
|
+
> **Note:** Setting `tools.bash` affects the _default_ for bash commands, but `bash` patterns can provide command-level overrides.
|
|
237
|
+
>
|
|
238
|
+
> **Note:** Setting `tools.mcp` controls coarse access to a registered `mcp` tool when one is available. Specific `mcp` rules still override it when a target pattern matches.
|
|
239
|
+
>
|
|
240
|
+
> **Note:** Top-level shorthand is only supported for the canonical Pi built-ins (`bash`, `read`, `write`, `edit`, `grep`, `find`, `ls`) in agent frontmatter. Use `permission.tools.<name>` for `mcp`, `task`, and any third-party tool.
|
|
241
|
+
|
|
242
|
+
### `bash`
|
|
243
|
+
|
|
244
|
+
Command patterns use `*` wildcards and match against the full command string. If multiple patterns match, the **last matching rule wins**, so put broad fallback rules first and specific overrides after them.
|
|
245
|
+
|
|
246
|
+
```jsonc
|
|
247
|
+
{
|
|
248
|
+
"bash": {
|
|
249
|
+
"git *": "ask",
|
|
250
|
+
"git status": "allow",
|
|
251
|
+
"rm -rf *": "deny",
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### `mcp`
|
|
257
|
+
|
|
258
|
+
MCP permissions match against derived targets from tool input. These rules are more specific than `tools.mcp` and override that fallback when a pattern matches:
|
|
259
|
+
|
|
260
|
+
| Target Type | Examples |
|
|
261
|
+
| ----------------- | --------------------------------------------------------------------- |
|
|
262
|
+
| Baseline ops | `mcp_status`, `mcp_list`, `mcp_search`, `mcp_describe`, `mcp_connect` |
|
|
263
|
+
| Server name | `myServer` |
|
|
264
|
+
| Server/tool combo | `myServer:search`, `myServer_search` |
|
|
265
|
+
| Generic | `mcp_call` |
|
|
266
|
+
|
|
267
|
+
```jsonc
|
|
268
|
+
{
|
|
269
|
+
"mcp": {
|
|
270
|
+
"mcp_status": "allow",
|
|
271
|
+
"mcp_list": "allow",
|
|
272
|
+
"myServer:*": "ask",
|
|
273
|
+
"dangerousServer": "deny",
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
> **Note:** Baseline discovery targets may auto-allow when you permit any MCP rule.
|
|
279
|
+
|
|
280
|
+
#### MCP Tool Fallback via `tools.mcp`
|
|
281
|
+
|
|
282
|
+
A registered `mcp` tool can use `tools.mcp` as an entry permission point. This provides a fallback when no specific MCP pattern matches:
|
|
283
|
+
|
|
284
|
+
```jsonc
|
|
285
|
+
{
|
|
286
|
+
"tools": {
|
|
287
|
+
"mcp": "allow",
|
|
288
|
+
},
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
This is useful for per-agent configurations where you want to grant MCP access broadly:
|
|
293
|
+
|
|
294
|
+
```yaml
|
|
295
|
+
# In the global Pi agents directory (default: ~/.pi/agent/agents/researcher.md; respects PI_CODING_AGENT_DIR)
|
|
296
|
+
---
|
|
297
|
+
name: researcher
|
|
298
|
+
permission:
|
|
299
|
+
tools:
|
|
300
|
+
mcp: allow
|
|
301
|
+
---
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The permission resolution order for MCP operations:
|
|
305
|
+
|
|
306
|
+
1. Specific `mcp` patterns (e.g., `myServer:toolName`, `myServer_*`)
|
|
307
|
+
2. `tools.mcp` fallback (if set)
|
|
308
|
+
3. `defaultPolicy.mcp`
|
|
309
|
+
|
|
310
|
+
### `skills`
|
|
311
|
+
|
|
312
|
+
Skill name patterns use `*` wildcards:
|
|
313
|
+
|
|
314
|
+
```jsonc
|
|
315
|
+
{
|
|
316
|
+
"skills": {
|
|
317
|
+
"*": "ask",
|
|
318
|
+
"dangerous-*": "deny",
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### `special`
|
|
324
|
+
|
|
325
|
+
Reserved permission checks:
|
|
326
|
+
|
|
327
|
+
| Key | Description |
|
|
328
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
329
|
+
| `doom_loop` | Controls doom loop detection behavior |
|
|
330
|
+
| `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 |
|
|
331
|
+
| `tool_call_limit` | _(schema only, not enforced yet)_ |
|
|
332
|
+
|
|
333
|
+
```jsonc
|
|
334
|
+
{
|
|
335
|
+
"special": {
|
|
336
|
+
"doom_loop": "deny",
|
|
337
|
+
"external_directory": "ask",
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
`external_directory` is evaluated before the normal tool permission check. For example, `tools.read: "allow"` can permit ordinary reads while `special.external_directory: "ask"` still requires confirmation before reading `../outside.txt` or an absolute path outside `ctx.cwd`. Optional-path search tools (`find`, `grep`, `ls`) skip this check when no `path` is provided because they default to the active working directory.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Common Recipes
|
|
347
|
+
|
|
348
|
+
### Read-Only Mode
|
|
349
|
+
|
|
350
|
+
```jsonc
|
|
351
|
+
{
|
|
352
|
+
"defaultPolicy": {
|
|
353
|
+
"tools": "ask",
|
|
354
|
+
"bash": "ask",
|
|
355
|
+
"mcp": "ask",
|
|
356
|
+
"skills": "ask",
|
|
357
|
+
"special": "ask",
|
|
358
|
+
},
|
|
359
|
+
"tools": {
|
|
360
|
+
"read": "allow",
|
|
361
|
+
"grep": "allow",
|
|
362
|
+
"find": "allow",
|
|
363
|
+
"ls": "allow",
|
|
364
|
+
"write": "deny",
|
|
365
|
+
"edit": "deny",
|
|
366
|
+
},
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Restricted Bash Surface
|
|
371
|
+
|
|
372
|
+
```jsonc
|
|
373
|
+
{
|
|
374
|
+
"defaultPolicy": {
|
|
375
|
+
"tools": "ask",
|
|
376
|
+
"bash": "deny",
|
|
377
|
+
"mcp": "ask",
|
|
378
|
+
"skills": "ask",
|
|
379
|
+
"special": "ask",
|
|
380
|
+
},
|
|
381
|
+
"bash": {
|
|
382
|
+
"git *": "ask",
|
|
383
|
+
"git status": "allow",
|
|
384
|
+
"git diff": "allow",
|
|
385
|
+
"git log *": "allow",
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### MCP Discovery Only
|
|
391
|
+
|
|
392
|
+
```jsonc
|
|
393
|
+
{
|
|
394
|
+
"defaultPolicy": {
|
|
395
|
+
"tools": "ask",
|
|
396
|
+
"bash": "ask",
|
|
397
|
+
"mcp": "ask",
|
|
398
|
+
"skills": "ask",
|
|
399
|
+
"special": "ask",
|
|
400
|
+
},
|
|
401
|
+
"mcp": {
|
|
402
|
+
"*": "ask",
|
|
403
|
+
"mcp_status": "allow",
|
|
404
|
+
"mcp_list": "allow",
|
|
405
|
+
"mcp_search": "allow",
|
|
406
|
+
"mcp_describe": "allow",
|
|
407
|
+
},
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Per-Agent Lockdown
|
|
412
|
+
|
|
413
|
+
In the global Pi agents directory (default: `~/.pi/agent/agents/reviewer.md`, respects `PI_CODING_AGENT_DIR`):
|
|
414
|
+
|
|
415
|
+
```yaml
|
|
416
|
+
---
|
|
417
|
+
permission:
|
|
418
|
+
tools:
|
|
419
|
+
write: deny
|
|
420
|
+
edit: deny
|
|
421
|
+
bash:
|
|
422
|
+
"*": deny
|
|
423
|
+
---
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Technical Details
|
|
429
|
+
|
|
430
|
+
### Permission Prompt Summaries
|
|
431
|
+
|
|
432
|
+
When a tool permission resolves to `ask`, the prompt is designed to be readable enough for an informed approval decision:
|
|
433
|
+
|
|
434
|
+
- `bash` prompts show the command and matched bash pattern when available.
|
|
435
|
+
- `mcp` prompts show the derived MCP target and matched rule when available.
|
|
436
|
+
- Built-in file tools show concise summaries, such as the target path and edit/write line counts, instead of raw multiline JSON.
|
|
437
|
+
- Unknown or third-party extension tools show a bounded single-line JSON preview of the input so users are not asked to approve a blind tool name.
|
|
438
|
+
|
|
439
|
+
Example edit approval prompt:
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
Current agent requested tool 'edit' for '.gitignore' (1 replacement: edit #1 replaces 5 lines with 2 lines). Allow this call?
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Subagent Permission Forwarding
|
|
446
|
+
|
|
447
|
+
When a delegated or routed subagent runs without direct UI access, `ask` permissions can still be enforced by forwarding the confirmation request through Pi session directories. The main interactive session polls for forwarded requests, shows the confirmation prompt, writes the response, and the subagent resumes once that decision is available.
|
|
448
|
+
|
|
449
|
+
This keeps `ask` policies usable even when the original permission check happens inside a non-UI execution context.
|
|
450
|
+
|
|
451
|
+
### Logging
|
|
452
|
+
|
|
453
|
+
When the extension prompts, denies, or forwards permission requests, it can append structured JSONL entries under:
|
|
454
|
+
|
|
455
|
+
```text
|
|
456
|
+
Default global logs directory: ~/.pi/agent/extensions/pi-permission-system/logs/
|
|
457
|
+
Actual global logs directory: $PI_CODING_AGENT_DIR/extensions/pi-permission-system/logs when PI_CODING_AGENT_DIR is set
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
- `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history, including bounded `toolInputPreview` values for non-bash/non-MCP tool calls
|
|
461
|
+
- `pi-permission-system-debug.jsonl` — disabled by default and intended for troubleshooting
|
|
462
|
+
|
|
463
|
+
On every session start, the extension emits a `config.resolved` entry to both logs listing the resolved config paths and whether each exists.
|
|
464
|
+
This makes it easy to verify which files the extension actually loaded:
|
|
465
|
+
|
|
466
|
+
```jsonc
|
|
467
|
+
{
|
|
468
|
+
"event": "config.resolved",
|
|
469
|
+
"extensionConfigPath": "/…/pi-permission-system/config.json",
|
|
470
|
+
"extensionConfigExists": true,
|
|
471
|
+
"globalConfigPath": "/…/.pi/agent/pi-permissions.jsonc",
|
|
472
|
+
"globalConfigExists": false,
|
|
473
|
+
"projectConfigPath": "/…/my-project/.pi/agent/pi-permissions.jsonc",
|
|
474
|
+
"projectConfigExists": true,
|
|
475
|
+
"agentsDir": "/…/.pi/agent/agents",
|
|
476
|
+
"agentsDirExists": true,
|
|
477
|
+
"projectAgentsDir": "/…/my-project/.pi/agent/agents",
|
|
478
|
+
"projectAgentsDirExists": false,
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Architecture
|
|
483
|
+
|
|
484
|
+
```text
|
|
485
|
+
index.ts → Root Pi entrypoint shim
|
|
486
|
+
src/
|
|
487
|
+
├── index.ts → Extension bootstrap, permission checks, readable prompts, review logging, reload handling, and subagent forwarding
|
|
488
|
+
├── config-reporter.ts → Resolved config path reporting for diagnostic logs
|
|
489
|
+
├── extension-config.ts → Extension-local config loading and default creation
|
|
490
|
+
├── logging.ts → File-only debug/review logging helpers
|
|
491
|
+
├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
|
|
492
|
+
├── skill-prompt-sanitizer.ts → Skill prompt parsing, multi-block sanitization, and skill-read path matching
|
|
493
|
+
├── bash-filter.ts → Bash command wildcard pattern matching
|
|
494
|
+
├── wildcard-matcher.ts → Shared wildcard pattern compilation and matching
|
|
495
|
+
├── common.ts → Shared utilities (YAML parsing, type guards, etc.)
|
|
496
|
+
├── tool-registry.ts → Registered tool name resolution
|
|
497
|
+
└── types.ts → TypeScript type definitions
|
|
498
|
+
tests/
|
|
499
|
+
├── permission-system.test.ts → Core permission, layering, forwarding, and policy tests
|
|
500
|
+
├── config-modal.test.ts → Config command and modal behavior tests
|
|
501
|
+
└── test-harness.ts → Shared lightweight test helpers
|
|
502
|
+
schemas/
|
|
503
|
+
└── permissions.schema.json → JSON Schema for policy validation
|
|
504
|
+
config/
|
|
505
|
+
└── config.example.json → Starter global policy template
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
#### Module Organization
|
|
509
|
+
|
|
510
|
+
The extension uses a modular architecture with shared utilities:
|
|
511
|
+
|
|
512
|
+
| Module | Purpose |
|
|
513
|
+
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
514
|
+
| `common.ts` | Shared utilities: `toRecord()`, `getNonEmptyString()`, `isPermissionState()`, `parseSimpleYamlMap()`, `extractFrontmatter()` |
|
|
515
|
+
| `wildcard-matcher.ts` | Compile-once wildcard patterns with last-match-wins evaluation: `compileWildcardPatterns()`, `findCompiledWildcardMatch()` |
|
|
516
|
+
| `permission-manager.ts` | Policy resolution with file stamp caching for performance |
|
|
517
|
+
| `bash-filter.ts` | Uses shared wildcard matcher for bash command patterns |
|
|
518
|
+
| `skill-prompt-sanitizer.ts` | Parses all available skill prompt blocks, removes denied skills, and tracks visible skill paths for read protection |
|
|
519
|
+
|
|
520
|
+
#### Performance Optimizations
|
|
521
|
+
|
|
522
|
+
- **File stamp caching**: Configurations are cached with file modification timestamps to avoid redundant reads
|
|
523
|
+
- **Pre-compiled patterns**: Wildcard patterns are compiled to regex once and reused across permission checks
|
|
524
|
+
- **Resolved permissions caching**: Merged agent+global permissions are cached per-agent with invalidation on file changes
|
|
525
|
+
|
|
526
|
+
### Threat Model
|
|
527
|
+
|
|
528
|
+
**Goal:** Enforce policy at the host level, not the model level.
|
|
529
|
+
|
|
530
|
+
**What this stops:**
|
|
531
|
+
|
|
532
|
+
- Agent calling tools it shouldn't use (e.g., `write`, dangerous `bash`)
|
|
533
|
+
- Tool switching attempts (calling non-existent tool names)
|
|
534
|
+
- Accidental escalation via skill loading
|
|
535
|
+
- Unapproved path-bearing tool access outside the active working directory when `external_directory` is `ask` or `deny`
|
|
536
|
+
|
|
537
|
+
**Limitations:**
|
|
538
|
+
|
|
539
|
+
- If a dangerous action is possible via an allowed tool, policy must explicitly restrict it
|
|
540
|
+
- This is a permission decision layer, not a sandbox
|
|
541
|
+
|
|
542
|
+
### Schema Validation
|
|
543
|
+
|
|
544
|
+
Validate your config against the included schema:
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
npx --yes ajv-cli@5 validate \
|
|
548
|
+
-s ./schemas/permissions.schema.json \
|
|
549
|
+
-d ./pi-permissions.valid.json
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**Editor tip:** Add `"$schema": "./schemas/permissions.schema.json"` to your config for autocomplete support.
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Troubleshooting
|
|
557
|
+
|
|
558
|
+
| Problem | Cause | Solution |
|
|
559
|
+
| ------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
560
|
+
| 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 |
|
|
561
|
+
| Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
|
|
562
|
+
| Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
|
|
563
|
+
| `/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. |
|
|
564
|
+
| External file path blocked | `special.external_directory` is `ask` without UI or `deny` | Allow/ask the special permission or keep file tools inside the active working directory. |
|
|
565
|
+
| Permission prompt is too verbose | Generic extension tool input is large | Built-in file tools are summarized automatically; third-party tools are capped to a bounded one-line JSON preview. |
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Development
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
npm run build # Type-check TypeScript (no emit)
|
|
573
|
+
npm run lint # Biome lint + format check
|
|
574
|
+
npm run lint:fix # Biome lint + format auto-fix
|
|
575
|
+
npm run lint:md # markdownlint-cli2 on README etc.
|
|
576
|
+
npm run lint:all # lint + lint:md
|
|
577
|
+
npm run format # Biome format --write
|
|
578
|
+
npm run test # Run tests from ./tests
|
|
579
|
+
npm run check # build + lint:all + test
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Pre-commit hooks
|
|
583
|
+
|
|
584
|
+
This project uses [prek](https://prek.j178.dev/) to run Biome and markdownlint on staged files before each commit.
|
|
585
|
+
This catches lint and formatting issues locally instead of waiting for CI.
|
|
586
|
+
|
|
587
|
+
1. Install prek ([installation guide](https://prek.j178.dev/installation/)).
|
|
588
|
+
2. Run `npm install` — the `prepare` script calls `prek install` automatically.
|
|
589
|
+
If prek is not installed, the script prints a warning and continues.
|
|
590
|
+
3. Hooks run automatically on `git commit`.
|
|
591
|
+
To skip in emergencies: `git commit --no-verify`.
|
|
592
|
+
|
|
593
|
+
The hook configuration lives in `prek.toml` at the repo root.
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Related Pi Extensions
|
|
598
|
+
|
|
599
|
+
- [pi-multi-auth](https://github.com/MasuRii/pi-multi-auth) — Multi-provider credential management and quota-aware rotation
|
|
600
|
+
- [pi-tool-display](https://github.com/MasuRii/pi-tool-display) — Compact tool rendering and diff visualization
|
|
601
|
+
- [pi-rtk-optimizer](https://github.com/MasuRii/pi-rtk-optimizer) — RTK command rewriting and output compaction
|
|
602
|
+
- [pi-MUST-have-extension](https://github.com/MasuRii/pi-MUST-have-extension) — RFC 2119 keyword normalization for prompts
|
|
603
|
+
|
|
604
|
+
## License
|
|
605
|
+
|
|
606
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"defaultPolicy": {
|
|
3
|
+
"tools": "ask",
|
|
4
|
+
"bash": "ask",
|
|
5
|
+
"mcp": "ask",
|
|
6
|
+
"skills": "ask",
|
|
7
|
+
"special": "ask"
|
|
8
|
+
},
|
|
9
|
+
"tools": {
|
|
10
|
+
"read": "allow",
|
|
11
|
+
"write": "deny"
|
|
12
|
+
},
|
|
13
|
+
"bash": {
|
|
14
|
+
"git status": "allow",
|
|
15
|
+
"git *": "ask"
|
|
16
|
+
},
|
|
17
|
+
"mcp": {
|
|
18
|
+
"mcp_status": "allow"
|
|
19
|
+
},
|
|
20
|
+
"skills": {
|
|
21
|
+
"*": "ask"
|
|
22
|
+
},
|
|
23
|
+
"special": {
|
|
24
|
+
"doom_loop": "deny",
|
|
25
|
+
"external_directory": "ask"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/index.ts
ADDED