mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
feat(permission): integrate auto-review and hard guards
This commit is contained in:
@@ -17,6 +17,8 @@ Permission enforcement extension for the [Pi](https://pi.mariozechner.at/) codin
|
||||
|
||||
- **Hides disallowed tools** before the agent starts — no wasted turns probing for blocked tools
|
||||
- **Enforces allow / ask / deny** at tool-call time with UI confirmation dialogs
|
||||
- **Reviews unmatched `ask` requests automatically** with an integrated Codex Guardian-style reviewer; only `defer` reaches the human fallback
|
||||
- **Applies a separate hard-guard policy first** so project rules, session grants, and model review cannot override high-risk denies
|
||||
- **Controls bash commands** with wildcard pattern matching (`git *: ask`, `rm -rf *: deny`)
|
||||
- **Gates MCP and skill access** at server, tool, and skill-name granularity
|
||||
- **Protects sensitive file patterns** — cross-cutting `path` rules deny `.env`, `~/.ssh/*`, etc. across all tools and bash at once, matching both the path as referenced and its symlink-resolved form so a deny cannot be evaded through a symlink alias
|
||||
@@ -34,42 +36,38 @@ pi install npm:@gotgenes/pi-permission-system
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Create the global config file at `~/.pi/agent/extensions/pi-permission-system/config.json`:
|
||||
1. Create ordinary policy at `~/.pi/agent/extensions/pi-permission-system/policy.jsonc`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"authorizerChain": ["auto-review"],
|
||||
"permission": {
|
||||
"*": "allow",
|
||||
"path": {
|
||||
"*": "allow",
|
||||
"*.env": "deny",
|
||||
"*.env.*": "deny",
|
||||
"*.env.example": "allow"
|
||||
},
|
||||
"*": "ask",
|
||||
"read": "allow",
|
||||
"bash": {
|
||||
"*": "ask",
|
||||
"rm -rf *": "deny",
|
||||
"sudo *": "ask"
|
||||
},
|
||||
"external_directory": "ask"
|
||||
"git status*": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Start Pi — the extension automatically loads and enforces your policy.
|
||||
2. Create independent hard guards at `~/.pi/agent/extensions/pi-permission-system/hard-guards.jsonc`:
|
||||
|
||||
All permissions use one of three states:
|
||||
```jsonc
|
||||
{
|
||||
"version": 1,
|
||||
"permission": {
|
||||
"*": "allow",
|
||||
"*.env": "deny",
|
||||
"*.env.*": "deny",
|
||||
"*.env.example": "allow",
|
||||
"~/.ssh/**": "deny"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| State | Behavior |
|
||||
| ------- | ---------------------------------------- |
|
||||
| `allow` | Permits the action silently |
|
||||
| `deny` | Blocks the action with an error message |
|
||||
| `ask` | Prompts the user for confirmation via UI |
|
||||
|
||||
When the dialog prompts, you can approve once or approve a pattern for the rest of the session.
|
||||
In an interactive TUI session the prompt is an inline keybind dialog — `y` approve, `s` approve for this session, `n` deny, `r` deny with a reason — where each hotkey arms and a second press confirms (configurable via `doublePressToConfirm`).
|
||||
The prompt shows one fact per line — who is asking, the tool, the matched rule, the value being decided — within a row budget, so a large tool input cannot take over the transcript; `Ctrl+O` (`app.tools.expand`) expands it to the complete request.
|
||||
See [docs/configuration.md](docs/configuration.md#inline-permission-dialog-tui) for the hotkeys and [docs/session-approvals.md](docs/session-approvals.md) for session-scoped rules and pattern suggestions.
|
||||
3. Restart Pi or run `/reload`. Ordinary `ask` requests now reach AutoReview first; only `defer` opens the human dialog.
|
||||
|
||||
The `path` surface is a cross-cutting gate that applies to **all** file access — Pi tools, bash commands, MCP calls, and extension tools alike.
|
||||
Extension and MCP tools that operate on paths (via `input.path`, MCP's `input.arguments.path`, or a registered access extractor) are gated by default, so a `path` deny cannot be overridden by a per-tool allow — making it the right place to protect sensitive files like `.env` or `~/.ssh/*` from every tool at once.
|
||||
@@ -102,34 +100,34 @@ See [docs/configuration.md](docs/configuration.md) for the full recipe.
|
||||
|
||||
## Configuration
|
||||
|
||||
Config lives in one JSON file per scope:
|
||||
The integrated extension uses two independent JSONC files:
|
||||
|
||||
| Scope | Path |
|
||||
| ------- | --------------------------------------------------------- |
|
||||
| Global | `~/.pi/agent/extensions/pi-permission-system/config.json` |
|
||||
| Project | `<cwd>/.pi/extensions/pi-permission-system/config.json` |
|
||||
| Purpose | Scope | Path |
|
||||
| ------- | ----- | ---- |
|
||||
| Ordinary policy, runtime knobs, AutoReview | Global | `~/.pi/agent/extensions/pi-permission-system/policy.jsonc` |
|
||||
| Ordinary policy override | Project | `<cwd>/.pi/extensions/pi-permission-system/policy.jsonc` |
|
||||
| Non-overridable hard guards | Global only | `~/.pi/agent/extensions/pi-permission-system/hard-guards.jsonc` |
|
||||
|
||||
Project overrides global; per-agent YAML frontmatter overrides both.
|
||||
Project config (policy and runtime knobs) is loaded only once the project is trusted — in an untrusted directory only global config applies, so an untrusted repository cannot loosen your global policy (see [Upgrading](#2200--project-config-requires-project-trust)).
|
||||
The bundle seeds missing files once with mode `0600` and never overwrites user edits. On first load, an existing legacy global `config.json` is copied to `policy.jsonc`; the legacy file is left untouched. Project policy is loaded only for a trusted project. Hard guards are never loaded from the project and are evaluated before ordinary policy and session grants.
|
||||
|
||||
Within a surface map like `bash` or `mcp`, **last matching rule wins** — put broad catch-alls first and specific overrides after.
|
||||
Ordinary policy remains flat `allow` / `ask` / `deny`. The bundle default asks for unmatched requests and names the built-in `auto-review` authorizer. The evaluation order is:
|
||||
|
||||
The optional `shellTools` field records which non-`bash` tools carry shell semantics (e.g. an `exec_command` tool that replaces native `bash`), so they are gated at full parity with native `bash` — see [docs/configuration.md](docs/configuration.md#shelltools--gating-aliased-shell-tools).
|
||||
Registered custom input previews for those aliases are preserved as authorization evidence and review-log context, allowing an extension to disclose execution context such as an SSH target without weakening bash enforcement.
|
||||
A shell alias may also set `decisionFloor: "ask"`: Bash `allow` results are raised into the configured authorizer chain, while existing `ask` and hard `deny` decisions remain unchanged.
|
||||
1. global hard guards (`deny` is final; `allow` is only an exception inside that file);
|
||||
2. ordinary global/project/per-agent policy and current session grants;
|
||||
3. built-in AutoReview for an ordinary `ask`;
|
||||
4. the existing human prompt only when AutoReview returns `defer`.
|
||||
|
||||
The optional `authorizerChain` field names registered case-by-case decision links (e.g. a light model judge) to consult when a request lands on `ask`, ahead of the interactive prompt.
|
||||
A downstream extension registers a link via `getPermissionsService().registerAuthorizer(name, authorize)`; it decides nothing until you name it here (opt-in), config order fixes the chain order, and the chain owner caps any link's `allow` on `external_directory`/`path` to keep it within your policy — see [docs/configuration.md](docs/configuration.md#authorizer-chain--case-by-case-decision-links).
|
||||
A subagent's ask is reviewed by the chain of the session serving it, one hop up, rather than inside the subagent — see the same section.
|
||||
[`@gotgenes/pi-permission-model-judge`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-model-judge) is a first-party reference implementation of such a link — a deny-first reviewer that auto-denies mistyped out-of-directory paths.
|
||||
`autoReview` and `analysis` are regular top-level keys in `policy.jsonc`. AutoReview defaults to `openai-codex/codex-auto-review`, low reasoning, and a 90-second budget. Model/config/authentication/timeout errors fail safe to `defer`.
|
||||
|
||||
For the full reference — all surfaces, runtime knobs, per-agent overrides, merge semantics, and common recipes — see [docs/configuration.md](docs/configuration.md).
|
||||
Completed model verdicts (`allow` and `deny`) are appended to an owner-only, sanitized count ledger. Rationales, transcripts, and raw tool payloads are not retained. After 500 new verdicts, `agent_settled` launches one isolated, tool-free background AgentSession using the configured review model. The validated proposal is persisted and delivered as a follow-up in the same conversation. It never edits either policy file automatically: the active agent must present the candidates and wait for the user's explicit selection and confirmation. A pending batch survives session shutdown and resumes in the next session.
|
||||
|
||||
For the full policy surface and merge semantics, see [docs/configuration.md](docs/configuration.md). The integrated reviewer provenance is recorded in [docs/auto-review/UPSTREAM.md](docs/auto-review/UPSTREAM.md).
|
||||
|
||||
## Upgrading
|
||||
|
||||
### 22.0.0 — project config requires project trust
|
||||
|
||||
Project-scoped configuration (the project `config.json` and project-agent frontmatter — both permission policy and runtime knobs such as `yoloMode`) is now loaded only when Pi reports the project as trusted.
|
||||
Project-scoped configuration (the project `policy.jsonc` and project-agent frontmatter — both permission policy and runtime knobs such as `yoloMode`) is loaded only when Pi reports the project as trusted.
|
||||
In an untrusted directory, only global config applies; a skip is surfaced with a warning and a `project_trust.skipped` review-log entry.
|
||||
Grant project trust (or set `defaultProjectTrust`) to load a project's config.
|
||||
See [docs/migration/0644-project-trust-gating.md](docs/migration/0644-project-trust-gating.md).
|
||||
|
||||
Reference in New Issue
Block a user