@gotgenes/pi-permission-system 2.0.0 → 3.0.1
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 +36 -0
- package/README.md +92 -35
- package/config/config.example.json +6 -0
- package/package.json +1 -1
- package/schemas/permissions.schema.json +114 -16
- package/src/active-agent.ts +58 -0
- package/src/config-loader.ts +398 -0
- package/src/config-paths.ts +34 -0
- package/src/config-reporter.ts +16 -8
- package/src/external-directory.ts +113 -0
- package/src/forwarded-permissions/io.ts +328 -0
- package/src/forwarded-permissions/polling.ts +334 -0
- package/src/index.ts +153 -1095
- package/src/permission-manager.ts +25 -111
- package/src/permission-prompts.ts +131 -0
- package/src/subagent-context.ts +52 -0
- package/src/tool-input-preview.ts +206 -0
- package/tests/active-agent.test.ts +160 -0
- package/tests/bash-filter.test.ts +137 -0
- package/tests/common.test.ts +189 -0
- package/tests/config-loader.test.ts +364 -0
- package/tests/config-paths.test.ts +78 -0
- package/tests/config-reporter.test.ts +42 -33
- package/tests/extension-config.test.ts +51 -0
- package/tests/external-directory.test.ts +250 -0
- package/tests/permission-prompts.test.ts +301 -0
- package/tests/permission-system.test.ts +9 -26
- package/tests/session-start.test.ts +8 -33
- package/tests/skill-prompt-sanitizer.test.ts +244 -0
- package/tests/subagent-context.test.ts +124 -0
- package/tests/system-prompt-sanitizer.test.ts +186 -0
- package/tests/tool-input-preview.test.ts +452 -0
- package/tests/tool-registry.test.ts +155 -0
- package/tests/wildcard-matcher.test.ts +180 -0
- package/tests/yolo-mode.test.ts +110 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,42 @@ 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
|
+
## [3.0.1](https://github.com/gotgenes/pi-permission-system/compare/v3.0.0...v3.0.1) (2026-05-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* add descriptions to all JSON schema entities ([cb3a7ce](https://github.com/gotgenes/pi-permission-system/commit/cb3a7ce5257111159312ebb95a9f75dd4e4a9527))
|
|
14
|
+
* enrich JSON schema with examples, defaults, and markdown descriptions ([6f38d7e](https://github.com/gotgenes/pi-permission-system/commit/6f38d7edf01b950f20e2fab8604a398bf725a6c4))
|
|
15
|
+
* plan index.ts split into focused modules ([#21](https://github.com/gotgenes/pi-permission-system/issues/21)) ([ccd736a](https://github.com/gotgenes/pi-permission-system/commit/ccd736a83a4af208044eec925c80555ee645e344))
|
|
16
|
+
* **retro:** add retro notes for issue [#10](https://github.com/gotgenes/pi-permission-system/issues/10) ([31e59d6](https://github.com/gotgenes/pi-permission-system/commit/31e59d6172da7b0e9f02894afd2ba66a292de168))
|
|
17
|
+
* **retro:** correct formatting friction attribution ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([2e96b7b](https://github.com/gotgenes/pi-permission-system/commit/2e96b7bc1007a2ef55f95b29c40b8417c2c6c52f))
|
|
18
|
+
* update plan with Phase 2 unit tests using DI and vitest mocks ([#21](https://github.com/gotgenes/pi-permission-system/issues/21)) ([ad7f5fe](https://github.com/gotgenes/pi-permission-system/commit/ad7f5feeb856c84142a2a4258a9e173c8006532d))
|
|
19
|
+
|
|
20
|
+
## [3.0.0](https://github.com/gotgenes/pi-permission-system/compare/v2.0.0...v3.0.0) (2026-05-03)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### ⚠ BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
* Config is now loaded from ~/.pi/agent/extensions/pi-permission-system/config.json (global) and <cwd>/.pi/extensions/pi-permission-system/config.json (project). Legacy paths are detected and merged with migration warnings.
|
|
26
|
+
* Config and log file paths move from the extension install directory and ~/.pi/agent/ to the extensions/<id>/ convention.
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* add config-paths module with new layout paths ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([532d2a1](https://github.com/gotgenes/pi-permission-system/commit/532d2a1f1d816c1cfba5419f7d0041382c848b31))
|
|
31
|
+
* add unified config loader ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([20143e0](https://github.com/gotgenes/pi-permission-system/commit/20143e0f7b608965d889433a6b9bbd6f9ab8b4cc))
|
|
32
|
+
* detect and merge legacy config paths ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([95046de](https://github.com/gotgenes/pi-permission-system/commit/95046de6a57d604c5f0d9fa8c13a64478ba15c89))
|
|
33
|
+
* implement config merge in unified loader ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([30b9afe](https://github.com/gotgenes/pi-permission-system/commit/30b9afe3d83940aa3ad708c9d6783bb2d4337743))
|
|
34
|
+
* update config-reporter for consolidated layout ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([96c9ef4](https://github.com/gotgenes/pi-permission-system/commit/96c9ef4964f9551b0fa89bbbc506f7660c055d74))
|
|
35
|
+
* wire index.ts to consolidated config layout ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([e7f8e5f](https://github.com/gotgenes/pi-permission-system/commit/e7f8e5f2fb094f0d291561258190d1892a1e6856))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### Documentation
|
|
39
|
+
|
|
40
|
+
* plan config layout consolidation ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([eb2924d](https://github.com/gotgenes/pi-permission-system/commit/eb2924d57655d06f67891574839aebfa0586a43d))
|
|
41
|
+
* **retro:** add retro notes for issue [#20](https://github.com/gotgenes/pi-permission-system/issues/20) ([4735f0c](https://github.com/gotgenes/pi-permission-system/commit/4735f0c646a0ede11c9a76083822969eb6ca4a8f))
|
|
42
|
+
* update schema, example, and docs for consolidated config ([#10](https://github.com/gotgenes/pi-permission-system/issues/10)) ([39b5c01](https://github.com/gotgenes/pi-permission-system/commit/39b5c01de1c8c721e998b244e9c825a6eb05f858))
|
|
43
|
+
|
|
8
44
|
## [2.0.0](https://github.com/gotgenes/pi-permission-system/compare/v1.2.1...v2.0.0) (2026-05-03)
|
|
9
45
|
|
|
10
46
|
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ Pi auto-discovers extensions in these paths.
|
|
|
48
48
|
|
|
49
49
|
### Quick Start
|
|
50
50
|
|
|
51
|
-
1. Create the global
|
|
51
|
+
1. Create the global config file (default: `~/.pi/agent/extensions/pi-permission-system/config.json`, respects `PI_CODING_AGENT_DIR`):
|
|
52
52
|
|
|
53
53
|
```jsonc
|
|
54
54
|
{
|
|
@@ -57,12 +57,12 @@ Pi auto-discovers extensions in these paths.
|
|
|
57
57
|
"bash": "ask",
|
|
58
58
|
"mcp": "ask",
|
|
59
59
|
"skills": "ask",
|
|
60
|
-
"special": "ask"
|
|
60
|
+
"special": "ask"
|
|
61
61
|
},
|
|
62
62
|
"tools": {
|
|
63
63
|
"read": "allow",
|
|
64
|
-
"write": "deny"
|
|
65
|
-
}
|
|
64
|
+
"write": "deny"
|
|
65
|
+
}
|
|
66
66
|
}
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -100,36 +100,60 @@ The extension integrates via Pi's lifecycle hooks:
|
|
|
100
100
|
|
|
101
101
|
## Configuration
|
|
102
102
|
|
|
103
|
-
###
|
|
103
|
+
### Config File
|
|
104
|
+
|
|
105
|
+
**Location:** one unified config file per scope, following the `pi-autoformat` convention:
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
| Scope | Path |
|
|
108
|
+
| ------- | ------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| Global | `~/.pi/agent/extensions/pi-permission-system/config.json` (respects `PI_CODING_AGENT_DIR`) |
|
|
110
|
+
| Project | `<cwd>/.pi/extensions/pi-permission-system/config.json` |
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
Project config overrides global config; per-agent frontmatter overrides both.
|
|
113
|
+
Object-shaped fields (`defaultPolicy`, `tools`, `bash`, `mcp`, `skills`, `special`) use shallow-merge (later source wins per-key).
|
|
114
|
+
Scalar fields (`debugLog`, `permissionReviewLog`, `yoloMode`) use simple replacement.
|
|
108
115
|
|
|
109
|
-
|
|
116
|
+
The config file combines runtime knobs and permission policy in one object:
|
|
117
|
+
|
|
118
|
+
```jsonc
|
|
110
119
|
{
|
|
120
|
+
"$schema": "https://raw.githubusercontent.com/gotgenes/pi-permission-system/main/schemas/permissions.schema.json",
|
|
121
|
+
|
|
122
|
+
// Runtime knobs
|
|
111
123
|
"debugLog": false,
|
|
112
124
|
"permissionReviewLog": true,
|
|
113
|
-
"yoloMode": false
|
|
125
|
+
"yoloMode": false,
|
|
126
|
+
|
|
127
|
+
// Policy
|
|
128
|
+
"defaultPolicy": {
|
|
129
|
+
"tools": "ask",
|
|
130
|
+
"bash": "ask",
|
|
131
|
+
"mcp": "ask",
|
|
132
|
+
"skills": "ask",
|
|
133
|
+
"special": "ask"
|
|
134
|
+
},
|
|
135
|
+
"tools": { "read": "allow", "write": "deny" },
|
|
136
|
+
"bash": { "git status": "allow", "git *": "ask" },
|
|
137
|
+
"mcp": { "mcp_status": "allow" },
|
|
138
|
+
"skills": { "*": "ask" },
|
|
139
|
+
"special": { "doom_loop": "deny", "external_directory": "ask" }
|
|
114
140
|
}
|
|
115
141
|
```
|
|
116
142
|
|
|
143
|
+
#### Runtime knobs
|
|
144
|
+
|
|
117
145
|
| Key | Default | Description |
|
|
118
146
|
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
|
|
119
147
|
| `debugLog` | `false` | Enables verbose diagnostic logging to `logs/pi-permission-system-debug.jsonl` |
|
|
120
148
|
| `permissionReviewLog` | `true` | Enables the permission request/denial review log at `logs/pi-permission-system-permission-review.jsonl` |
|
|
121
149
|
| `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
|
|
122
150
|
|
|
123
|
-
Both logs write to
|
|
151
|
+
Both logs write to `~/.pi/agent/extensions/pi-permission-system/logs/`.
|
|
152
|
+
No debug output is printed to the terminal.
|
|
124
153
|
|
|
125
|
-
|
|
126
|
-
> The extension warns at startup when it detects misplaced keys.
|
|
154
|
+
#### Policy sections
|
|
127
155
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
**Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
|
|
131
|
-
|
|
132
|
-
The policy file is a JSON object with these sections:
|
|
156
|
+
The config file is a JSON object with these policy sections:
|
|
133
157
|
|
|
134
158
|
| Section | Description |
|
|
135
159
|
| --------------- | ---------------------------------------------------------------------------- |
|
|
@@ -169,21 +193,22 @@ permission:
|
|
|
169
193
|
|
|
170
194
|
**Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
|
|
171
195
|
|
|
172
|
-
### Project-Level
|
|
196
|
+
### Project-Level Config and Overrides
|
|
173
197
|
|
|
174
|
-
|
|
198
|
+
Project-local config uses the same format as the global config file.
|
|
199
|
+
Per-agent overrides use YAML frontmatter in the project agents directory:
|
|
175
200
|
|
|
176
|
-
| Scope | Path
|
|
177
|
-
| ---------------------- |
|
|
178
|
-
| Project
|
|
179
|
-
| Project agent override | `<cwd>/.pi/agent/agents/<agent>.md`
|
|
201
|
+
| Scope | Path |
|
|
202
|
+
| ---------------------- | ------------------------------------------------------------- |
|
|
203
|
+
| Project config | `<cwd>/.pi/extensions/pi-permission-system/config.json` |
|
|
204
|
+
| Project agent override | `<cwd>/.pi/agent/agents/<agent>.md` |
|
|
180
205
|
|
|
181
|
-
|
|
206
|
+
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`.
|
|
182
207
|
|
|
183
208
|
**Precedence order:**
|
|
184
209
|
|
|
185
|
-
1. Global
|
|
186
|
-
2. Project
|
|
210
|
+
1. Global config file
|
|
211
|
+
2. Project config file
|
|
187
212
|
3. Global agent frontmatter
|
|
188
213
|
4. Project agent frontmatter
|
|
189
214
|
|
|
@@ -454,7 +479,7 @@ When the extension prompts, denies, or forwards permission requests, it can appe
|
|
|
454
479
|
|
|
455
480
|
```text
|
|
456
481
|
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
|
|
482
|
+
Actual global logs directory: $PI_CODING_AGENT_DIR/extensions/pi-permission-system/logs/ when PI_CODING_AGENT_DIR is set
|
|
458
483
|
```
|
|
459
484
|
|
|
460
485
|
- `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
|
|
@@ -466,16 +491,17 @@ This makes it easy to verify which files the extension actually loaded:
|
|
|
466
491
|
```jsonc
|
|
467
492
|
{
|
|
468
493
|
"event": "config.resolved",
|
|
469
|
-
"
|
|
470
|
-
"
|
|
471
|
-
"
|
|
472
|
-
"
|
|
473
|
-
"projectConfigPath": "/…/my-project/.pi/agent/pi-permissions.jsonc",
|
|
474
|
-
"projectConfigExists": true,
|
|
494
|
+
"globalConfigPath": "/…/.pi/agent/extensions/pi-permission-system/config.json",
|
|
495
|
+
"globalConfigExists": true,
|
|
496
|
+
"projectConfigPath": "/…/my-project/.pi/extensions/pi-permission-system/config.json",
|
|
497
|
+
"projectConfigExists": false,
|
|
475
498
|
"agentsDir": "/…/.pi/agent/agents",
|
|
476
499
|
"agentsDirExists": true,
|
|
477
500
|
"projectAgentsDir": "/…/my-project/.pi/agent/agents",
|
|
478
501
|
"projectAgentsDirExists": false,
|
|
502
|
+
"legacyGlobalPolicyDetected": false,
|
|
503
|
+
"legacyProjectPolicyDetected": false,
|
|
504
|
+
"legacyExtensionConfigDetected": false
|
|
479
505
|
}
|
|
480
506
|
```
|
|
481
507
|
|
|
@@ -485,8 +511,10 @@ This makes it easy to verify which files the extension actually loaded:
|
|
|
485
511
|
index.ts → Root Pi entrypoint shim
|
|
486
512
|
src/
|
|
487
513
|
├── index.ts → Extension bootstrap, permission checks, readable prompts, review logging, reload handling, and subagent forwarding
|
|
514
|
+
├── config-loader.ts → Unified config loader, merger, and legacy-path detection
|
|
515
|
+
├── config-paths.ts → Path derivation for global, project, and legacy config locations
|
|
488
516
|
├── config-reporter.ts → Resolved config path reporting for diagnostic logs
|
|
489
|
-
├── extension-config.ts →
|
|
517
|
+
├── extension-config.ts → Runtime config normalization and defaults
|
|
490
518
|
├── logging.ts → File-only debug/review logging helpers
|
|
491
519
|
├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
|
|
492
520
|
├── skill-prompt-sanitizer.ts → Skill prompt parsing, multi-block sanitization, and skill-read path matching
|
|
@@ -553,11 +581,40 @@ npx --yes ajv-cli@5 validate \
|
|
|
553
581
|
|
|
554
582
|
---
|
|
555
583
|
|
|
584
|
+
## Migration from pre-v2 layout
|
|
585
|
+
|
|
586
|
+
Before v2, config was split across two files:
|
|
587
|
+
|
|
588
|
+
- Policy: `~/.pi/agent/pi-permissions.jsonc`
|
|
589
|
+
- Runtime knobs: `<extension-install-dir>/config.json`
|
|
590
|
+
|
|
591
|
+
These are now consolidated into one file.
|
|
592
|
+
The extension detects legacy files and merges them with a warning for one release.
|
|
593
|
+
To migrate manually:
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
# Move the global policy file
|
|
597
|
+
mkdir -p ~/.pi/agent/extensions/pi-permission-system
|
|
598
|
+
mv ~/.pi/agent/pi-permissions.jsonc ~/.pi/agent/extensions/pi-permission-system/config.json
|
|
599
|
+
|
|
600
|
+
# If you had project-level policy:
|
|
601
|
+
mkdir -p .pi/extensions/pi-permission-system
|
|
602
|
+
mv .pi/agent/pi-permissions.jsonc .pi/extensions/pi-permission-system/config.json
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Then add any runtime knobs (`debugLog`, `permissionReviewLog`, `yoloMode`) to the same file.
|
|
606
|
+
The old extension-root `config.json` is no longer read from the install directory.
|
|
607
|
+
|
|
608
|
+
> **Note:** Logs also moved from `<extension-install-dir>/logs/` to `~/.pi/agent/extensions/pi-permission-system/logs/`.
|
|
609
|
+
> Old log files are not deleted or migrated — they remain readable but no new entries are appended.
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
556
613
|
## Troubleshooting
|
|
557
614
|
|
|
558
615
|
| Problem | Cause | Solution |
|
|
559
616
|
| ------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
560
|
-
| Config not applied (everything asks) | File not found or parse error | Verify the global
|
|
617
|
+
| Config not applied (everything asks) | File not found or parse error | Verify the global config at `~/.pi/agent/extensions/pi-permission-system/config.json` (respects `PI_CODING_AGENT_DIR`); check for trailing commas |
|
|
561
618
|
| Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
|
|
562
619
|
| Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
|
|
563
620
|
| `/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. |
|
package/package.json
CHANGED
|
@@ -1,71 +1,169 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://pi-
|
|
4
|
-
"title": "PI Permission Configuration",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/gotgenes/pi-permission-system/main/schemas/permissions.schema.json",
|
|
4
|
+
"title": "PI Permission System Configuration",
|
|
5
|
+
"description": "Unified config file combining runtime knobs and permission policy for pi-permission-system.",
|
|
6
|
+
"markdownDescription": "Unified config file combining runtime knobs and permission policy for [pi-permission-system](https://github.com/gotgenes/pi-permission-system).\n\nPlace at `~/.pi/agent/extensions/pi-permission-system/config.json` (global) or `<project>/.pi/extensions/pi-permission-system/config.json` (project).",
|
|
5
7
|
"type": "object",
|
|
6
8
|
"additionalProperties": false,
|
|
7
9
|
"properties": {
|
|
8
10
|
"$schema": {
|
|
11
|
+
"description": "JSON Schema URI for editor autocomplete and validation.",
|
|
9
12
|
"type": "string"
|
|
10
13
|
},
|
|
14
|
+
"debugLog": {
|
|
15
|
+
"description": "Write verbose permission-system diagnostics to the extension logs directory.",
|
|
16
|
+
"markdownDescription": "Write verbose permission-system diagnostics to `logs/pi-permission-system-debug.jsonl` under the extension config directory.",
|
|
17
|
+
"type": "boolean",
|
|
18
|
+
"default": false
|
|
19
|
+
},
|
|
20
|
+
"permissionReviewLog": {
|
|
21
|
+
"description": "Write permission request and decision audit events to the extension logs directory.",
|
|
22
|
+
"markdownDescription": "Write permission request and decision audit events to `logs/pi-permission-system-permission-review.jsonl` under the extension config directory.",
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": true
|
|
25
|
+
},
|
|
26
|
+
"yoloMode": {
|
|
27
|
+
"description": "Auto-approve ask-state permission checks, including subagent approval forwarding.",
|
|
28
|
+
"markdownDescription": "Auto-approve `ask`-state permission checks, including subagent approval forwarding.\n\n⚠️ **Use with caution** — this disables all interactive confirmation prompts.",
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"default": false
|
|
31
|
+
},
|
|
11
32
|
"defaultPolicy": {
|
|
33
|
+
"description": "Fallback permission state per category when no specific rule matches.",
|
|
34
|
+
"markdownDescription": "Fallback permission state per category when no specific rule matches.\n\nAll fields default to `\"ask\"` when omitted.",
|
|
12
35
|
"type": "object",
|
|
13
36
|
"additionalProperties": false,
|
|
14
|
-
"required": ["tools", "bash", "mcp", "skills"],
|
|
15
37
|
"properties": {
|
|
16
38
|
"tools": {
|
|
17
|
-
"
|
|
39
|
+
"description": "Default permission for registered tools when no exact-name rule matches in the tools map.",
|
|
40
|
+
"markdownDescription": "Default permission for registered tools when no exact-name rule matches in the `tools` map.",
|
|
41
|
+
"$ref": "#/$defs/permissionState",
|
|
42
|
+
"default": "ask"
|
|
18
43
|
},
|
|
19
44
|
"bash": {
|
|
20
|
-
"
|
|
45
|
+
"description": "Default permission for bash commands when no pattern in the bash map matches.",
|
|
46
|
+
"markdownDescription": "Default permission for bash commands when no pattern in the `bash` map matches.",
|
|
47
|
+
"$ref": "#/$defs/permissionState",
|
|
48
|
+
"default": "ask"
|
|
21
49
|
},
|
|
22
50
|
"mcp": {
|
|
23
|
-
"
|
|
51
|
+
"description": "Default permission for MCP operations when no target pattern in the mcp map matches.",
|
|
52
|
+
"markdownDescription": "Default permission for MCP operations when no target pattern in the `mcp` map matches.",
|
|
53
|
+
"$ref": "#/$defs/permissionState",
|
|
54
|
+
"default": "ask"
|
|
24
55
|
},
|
|
25
56
|
"skills": {
|
|
26
|
-
"
|
|
57
|
+
"description": "Default permission for skill loading when no pattern in the skills map matches.",
|
|
58
|
+
"markdownDescription": "Default permission for skill loading when no pattern in the `skills` map matches.",
|
|
59
|
+
"$ref": "#/$defs/permissionState",
|
|
60
|
+
"default": "ask"
|
|
27
61
|
},
|
|
28
62
|
"special": {
|
|
29
|
-
"
|
|
63
|
+
"description": "Default permission for special checks (doom_loop, external_directory) when no explicit rule matches.",
|
|
64
|
+
"markdownDescription": "Default permission for special checks (`doom_loop`, `external_directory`) when no explicit rule matches.",
|
|
65
|
+
"$ref": "#/$defs/permissionState",
|
|
66
|
+
"default": "ask"
|
|
30
67
|
}
|
|
31
68
|
}
|
|
32
69
|
},
|
|
33
70
|
"tools": {
|
|
34
71
|
"description": "Exact-name permissions for registered tools. Use this map for the canonical Pi built-ins and any extension-provided or third-party tools.",
|
|
35
|
-
"
|
|
72
|
+
"markdownDescription": "Exact-name permissions for registered tools. Use this map for the canonical Pi built-ins (`read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`) and any extension-provided or third-party tools.\n\nNo wildcards — keys must match the registered tool name exactly.",
|
|
73
|
+
"$ref": "#/$defs/permissionMap",
|
|
74
|
+
"examples": [
|
|
75
|
+
{
|
|
76
|
+
"read": "allow",
|
|
77
|
+
"write": "deny",
|
|
78
|
+
"edit": "ask",
|
|
79
|
+
"mcp": "allow"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
36
82
|
},
|
|
37
83
|
"bash": {
|
|
38
|
-
"
|
|
84
|
+
"description": "Wildcard pattern permissions for bash commands. Patterns use * globs matched against the full command string. Last matching rule wins.",
|
|
85
|
+
"markdownDescription": "Wildcard pattern permissions for bash commands. Patterns use `*` globs matched against the full command string.\n\n**Last matching rule wins** — put broad catch-all rules first and specific overrides after them.",
|
|
86
|
+
"$ref": "#/$defs/permissionMap",
|
|
87
|
+
"examples": [
|
|
88
|
+
{
|
|
89
|
+
"git status": "allow",
|
|
90
|
+
"git diff": "allow",
|
|
91
|
+
"git *": "ask",
|
|
92
|
+
"rm -rf *": "deny"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
39
95
|
},
|
|
40
96
|
"mcp": {
|
|
41
|
-
"description": "Pattern-based permissions for targets invoked through a registered
|
|
42
|
-
"
|
|
97
|
+
"description": "Pattern-based permissions for targets invoked through a registered mcp tool when available.",
|
|
98
|
+
"markdownDescription": "Pattern-based permissions for targets invoked through a registered `mcp` tool when available.\n\nTargets include server names (`myServer`), qualified tool names (`myServer:search`, `myServer_search`), and baseline operations (`mcp_status`, `mcp_list`, `mcp_connect`, `mcp_describe`, `mcp_search`).",
|
|
99
|
+
"$ref": "#/$defs/permissionMap",
|
|
100
|
+
"examples": [
|
|
101
|
+
{
|
|
102
|
+
"mcp_status": "allow",
|
|
103
|
+
"mcp_list": "allow",
|
|
104
|
+
"myServer:*": "ask",
|
|
105
|
+
"dangerousServer": "deny"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
43
108
|
},
|
|
44
109
|
"skills": {
|
|
45
|
-
"
|
|
110
|
+
"description": "Wildcard pattern permissions for skill names. Controls which skills can be loaded or read from disk.",
|
|
111
|
+
"markdownDescription": "Wildcard pattern permissions for skill names. Controls which skills can be loaded or read from disk.\n\nUse `\"*\": \"ask\"` to require confirmation for all skills, or `\"dangerous-*\": \"deny\"` to block a family of skills.",
|
|
112
|
+
"$ref": "#/$defs/permissionMap",
|
|
113
|
+
"examples": [
|
|
114
|
+
{
|
|
115
|
+
"*": "ask",
|
|
116
|
+
"dangerous-*": "deny"
|
|
117
|
+
}
|
|
118
|
+
]
|
|
46
119
|
},
|
|
47
120
|
"special": {
|
|
121
|
+
"description": "Reserved permission checks for special runtime behaviors.",
|
|
48
122
|
"type": "object",
|
|
49
123
|
"additionalProperties": false,
|
|
50
124
|
"properties": {
|
|
51
125
|
"doom_loop": {
|
|
126
|
+
"description": "Controls doom loop detection behavior.",
|
|
52
127
|
"$ref": "#/$defs/permissionState"
|
|
53
128
|
},
|
|
54
129
|
"external_directory": {
|
|
130
|
+
"description": "Enforces permission checks for path-bearing file tools (read, write, edit, find, grep, ls) when they target paths outside the active working directory.",
|
|
131
|
+
"markdownDescription": "Enforces permission checks for path-bearing file tools (`read`, `write`, `edit`, `find`, `grep`, `ls`) when they target paths outside the active working directory.\n\nEvaluated **before** the normal tool permission check — so `tools.read: \"allow\"` can permit ordinary reads while `external_directory: \"ask\"` still requires confirmation for paths outside `cwd`.",
|
|
132
|
+
"$ref": "#/$defs/permissionState"
|
|
133
|
+
},
|
|
134
|
+
"tool_call_limit": {
|
|
135
|
+
"description": "Deprecated and ignored. This key has no effect and should be removed from your config.",
|
|
136
|
+
"markdownDescription": "⚠️ **Deprecated and ignored.** This key has no effect and should be removed from your config.",
|
|
137
|
+
"deprecated": true,
|
|
55
138
|
"$ref": "#/$defs/permissionState"
|
|
56
139
|
}
|
|
57
140
|
}
|
|
58
141
|
}
|
|
59
142
|
},
|
|
60
|
-
"required": ["defaultPolicy"],
|
|
61
143
|
"$defs": {
|
|
62
144
|
"permissionState": {
|
|
63
|
-
"
|
|
64
|
-
"
|
|
145
|
+
"description": "A permission decision: allow (permit silently), deny (block with error), or ask (prompt the user for confirmation).",
|
|
146
|
+
"oneOf": [
|
|
147
|
+
{
|
|
148
|
+
"const": "allow",
|
|
149
|
+
"description": "Permit the action silently with no user interaction."
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"const": "deny",
|
|
153
|
+
"description": "Block the action with an error message. The agent is told not to retry."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"const": "ask",
|
|
157
|
+
"description": "Prompt the user for confirmation via the interactive UI before proceeding."
|
|
158
|
+
}
|
|
159
|
+
]
|
|
65
160
|
},
|
|
66
161
|
"permissionMap": {
|
|
162
|
+
"description": "A map of name or wildcard patterns to permission states. Keys are matched against the relevant target (tool name, bash command, MCP target, or skill name).",
|
|
163
|
+
"markdownDescription": "A map of name or wildcard patterns to permission states.\n\nKeys are matched against the relevant target. Use `*` for wildcard matching. When multiple patterns match, the **last matching rule wins**.",
|
|
67
164
|
"type": "object",
|
|
68
165
|
"propertyNames": {
|
|
166
|
+
"description": "A non-empty pattern string. Use * for wildcard matching.",
|
|
69
167
|
"type": "string",
|
|
70
168
|
"minLength": 1
|
|
71
169
|
},
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Matches the `<active_agent name="...">` tag injected by pi-agent-router
|
|
5
|
+
* into the system prompt to identify which agent definition is active.
|
|
6
|
+
*/
|
|
7
|
+
export const ACTIVE_AGENT_TAG_REGEX =
|
|
8
|
+
/<active_agent\s+name=["']([^"']+)["'][^>]*>/i;
|
|
9
|
+
|
|
10
|
+
export function normalizeAgentName(value: unknown): string | null {
|
|
11
|
+
if (typeof value !== "string") {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
return trimmed ? trimmed : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getActiveAgentName(ctx: ExtensionContext): string | null {
|
|
20
|
+
const entries = ctx.sessionManager.getEntries();
|
|
21
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
22
|
+
const entry = entries[i] as {
|
|
23
|
+
type: string;
|
|
24
|
+
customType?: string;
|
|
25
|
+
data?: unknown;
|
|
26
|
+
};
|
|
27
|
+
if (entry.type !== "custom" || entry.customType !== "active_agent") {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const data = entry.data as { name?: unknown } | undefined;
|
|
32
|
+
const normalizedName = normalizeAgentName(data?.name);
|
|
33
|
+
if (normalizedName) {
|
|
34
|
+
return normalizedName;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (data?.name === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getActiveAgentNameFromSystemPrompt(
|
|
46
|
+
systemPrompt: string | undefined,
|
|
47
|
+
): string | null {
|
|
48
|
+
if (!systemPrompt) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const match = systemPrompt.match(ACTIVE_AGENT_TAG_REGEX);
|
|
53
|
+
if (!match?.[1]) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return normalizeAgentName(match[1]);
|
|
58
|
+
}
|