feat(permission): integrate auto-review and hard guards

This commit is contained in:
云服务部-叶林立
2026-09-02 15:34:29 +08:00
parent a4807dde67
commit 180da95ab7
79 changed files with 4263 additions and 9203 deletions
+43 -32
View File
@@ -2,29 +2,21 @@
## Config File Locations
One unified config file per scope:
Pi Permission System has an ordinary JSONC policy and an independent global hard-guard JSONC policy:
| Scope | Path |
| ------- | ------------------------------------------------------------------------------------------ |
| Global | `~/.pi/agent/extensions/pi-permission-system/config.json` (respects `PI_CODING_AGENT_DIR`) |
| Project | `<cwd>/.pi/extensions/pi-permission-system/config.json` |
| Purpose | Scope | Path |
| ------- | ----- | ---- |
| Ordinary policy + runtime | Global | `~/.pi/agent/extensions/pi-permission-system/policy.jsonc` |
| Ordinary policy override | Project | `<cwd>/.pi/extensions/pi-permission-system/policy.jsonc` |
| Hard guards | Global only | `~/.pi/agent/extensions/pi-permission-system/hard-guards.jsonc` |
Project config overrides global config; per-agent frontmatter overrides both.
All paths respect `PI_CODING_AGENT_DIR`. The bundle creates missing global files once with owner-only permissions. It does not overwrite an existing file. If `policy.jsonc` is absent but the old global `config.json` exists, the bundle copies the old content into `policy.jsonc` and leaves the source file intact.
**Project config requires project trust.**
Project and project-agent scopes (both permission policy and runtime config such as `yoloMode`) are loaded only when Pi reports the project as trusted (`ctx.isProjectTrusted()`).
In an untrusted directory, only global (and global-agent) config applies, so an untrusted repository cannot loosen your global policy; the extension surfaces a loud warning plus a `project_trust.skipped` review-log entry when it skips a project scope.
Grant project trust (or configure `defaultProjectTrust`) to load the project's config; a trust grant reloads project policy on the next `resources_discover` reload.
See [migration/0644-project-trust-gating.md](migration/0644-project-trust-gating.md).
Project policy overrides global ordinary policy and per-agent YAML frontmatter overrides both. Project policy and project agent definitions are loaded only when Pi reports that project as trusted.
> **Coming from OpenCode?**
> This extension's permission model was inspired by OpenCode's.
> See [OpenCode Compatibility](opencode-compatibility.md) for shared concepts, divergences, and a porting guide.
Hard guards are deliberately outside this merge. They are global-only and evaluated first, without session rules. A matching hard `deny` is final and cannot be loosened by project policy, per-agent policy, a session grant, or AutoReview. A hard-file `allow` can only form a narrower exception to another pattern in that same hard-guard file. `ask` is invalid in the hard-guard file.
<!-- -->
> **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.
> `~/.pi/agent` is the default. When `PI_CODING_AGENT_DIR` is set, Pi uses that directory instead.
## Merge Precedence
@@ -65,8 +57,22 @@ This clamp is deny-preserving and, like `yoloMode`, applied at composition; when
"exec_command": { "commandArgument": "cmd", "workdirArgument": "workdir" }
},
// Ordered names of registered live-authority chain links (empty = none)
"authorizerChain": [],
// Unmatched ordinary asks are reviewed before the terminal human prompt.
"authorizerChain": ["auto-review"],
"autoReview": {
"enabled": true,
"provider": "openai-codex",
"model": "codex-auto-review",
"reasoning": "low",
"timeoutMs": 90000,
"includeBaselinePolicy": true
},
"analysis": {
"enabled": true,
"threshold": 500,
"retainAnalyzedBatches": 2,
"timeoutMs": 90000
},
// Flat permission policy
"permission": {
@@ -95,6 +101,18 @@ This clamp is deny-preserving and, like `yoloMode`, applied at composition; when
> **Note:** Trailing commas are **not** supported.
> If parsing fails, the extension falls back to `ask` for all categories.
## Built-in AutoReview and Count-Based Analysis
`autoReview.enabled` controls the built-in authorizer named `auto-review`. It only receives requests that ordinary policy resolved to `ask`; it cannot bypass a hard deny. Add `"auto-review"` to `authorizerChain` to make it the primary case-by-case authority. An `allow` or `deny` verdict is final for that request. `defer` continues to the next authorizer and ultimately the human permission dialog.
The reviewer uses only trusted direct-user transcript evidence and recognized structured answers. Assistant, tool, compaction, and cancelled-answer content cannot authorize an operation. Model lookup, authentication, timeout, malformed output, or internal errors all return `defer`.
`analysis.threshold` counts completed automatic verdicts, not Pi turns and not deferrals. Each `allow` or `deny` appends one sanitized record under the global extension state directory. Records contain bounded surface/pattern fingerprints and risk/authorization classifications; they omit rationale, raw payload, transcript, and credentials.
When the count since the last accepted checkpoint reaches the threshold (500 by default), the ledger marks analysis pending. At the next `agent_settled` boundary the extension starts an isolated AgentSession with no tools, extensions, skills, prompt templates, themes, or project context. Its JSON proposal is schema-validated and filtered so authorization-dependent, denied, or high-risk evidence cannot become an unconditional allow candidate.
A successful proposal advances the checkpoint, is retained according to `retainAnalyzedBatches`, and is delivered with `deliverAs: "followUp"` and `triggerTurn: true` to this conversation. The proposal is advisory only. Neither `policy.jsonc` nor `hard-guards.jsonc` is modified until the user explicitly chooses and confirms rules. Shutdown aborts the running worker but leaves the pending checkpoint durable for the next session.
## Runtime Knobs
| Key | Default | Description |
@@ -1010,18 +1028,11 @@ Practical guidance:
## Schema Validation
Validate your config against the included schema:
The package includes two schemas:
```bash
npx --yes ajv-cli@5 validate \
-s ./schemas/permissions.schema.json \
-d ./config.json
```
- `schemas/permissions.schema.json` for `policy.jsonc`;
- `schemas/hard-guards.schema.json` for `hard-guards.jsonc`.
**Editor tip:** Add the hosted schema URL as the `$schema` key in your config for autocomplete and validation support:
Associate those packaged files with the corresponding JSONC filenames in your editor. Command-line validators that only accept JSON must receive a comment-stripped temporary copy; do not rewrite the user's JSONC merely to validate it.
```json
"$schema": "https://raw.githubusercontent.com/gotgenes/pi-packages/main/packages/pi-permission-system/schemas/permissions.schema.json"
```
The schema is generated from the extension's zod source of truth (`src/config-schema.ts`); regenerate it with `pnpm run gen:schema` after changing the config shape.
The ordinary policy schema is generated from `src/config-schema.ts`; regenerate it with `npm run gen:schema` after changing that shape. Keep the hard-guard schema synchronized with `HardGuardPolicyLoader` whenever its accepted document changes.