mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor permission system source
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
# OpenCode Compatibility
|
||||
|
||||
This extension's flat permission format and evaluation semantics were directly inspired by [OpenCode's permission model](https://opencode.ai/docs/permissions/) (v1.1.x permission rework).
|
||||
If you are familiar with OpenCode's permission system, most concepts transfer directly — the same mental model applies.
|
||||
|
||||
> **Point-in-time reference.**
|
||||
> This comparison reflects OpenCode as of May 2026.
|
||||
> See the [official OpenCode permissions docs](https://opencode.ai/docs/permissions/) for the latest upstream behavior.
|
||||
|
||||
## What Transfers Directly
|
||||
|
||||
The following concepts are shared between OpenCode and this extension:
|
||||
|
||||
| Concept | Description |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| Three actions | `allow` / `ask` / `deny` — identical semantics |
|
||||
| Flat `permission` object | Top-level key in config; surface names as keys |
|
||||
| `"*"` universal fallback | Sets the default action when no surface-specific rule matches |
|
||||
| Granular object syntax | Surface key → string (catch-all) or `{ pattern: action }` map |
|
||||
| Last-match-wins | When multiple patterns match, the last one in config order wins |
|
||||
| `*` wildcard | Matches zero or more of any character (including path separators) |
|
||||
| `?` wildcard | Matches exactly one character |
|
||||
| Home directory expansion | `~/`, `$HOME/`, and `${HOME}/` expand to the OS home directory in patterns |
|
||||
| `external_directory` surface | Gates access to paths outside the working directory |
|
||||
| `bash` surface | Command patterns matched against shell commands |
|
||||
| `skill` surface | Skill name patterns matched against skill invocations |
|
||||
| `task` surface | Gates subagent/delegation tool calls |
|
||||
| Session-scoped approvals | `once` / `always` / `reject` from the ask dialog; `always` adds a session rule |
|
||||
| Per-agent overrides | Override global permissions for specific agents |
|
||||
| Tool hiding | Denied tools are removed before the agent starts (no wasted turns probing) |
|
||||
| Bash path extraction | Tree-sitter AST parsing to detect external paths in shell commands (see [details below](#bash-path-extraction)) |
|
||||
| Bash arity table | Generates smart approval pattern suggestions (e.g., `git checkout *` not `git *`) |
|
||||
| Trailing wildcard optionality | `"ls *"` matches bare `"ls"` — the trailing `*` is optional |
|
||||
|
||||
If your OpenCode config uses these features, the equivalent works in this extension with minimal translation (see [Porting Guide](#porting-an-opencode-config) below).
|
||||
|
||||
## Where They Diverge
|
||||
|
||||
### Summary Table
|
||||
|
||||
| Area | OpenCode | This extension |
|
||||
| -------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Default fallback | `"*": "allow"` (permissive) | `"*": "ask"` (least privilege) |
|
||||
| `.env` file protection | Built-in `read` rules deny/ask `.env` files | No built-in rules; user configures with the cross-cutting `path` surface or per-tool path patterns (see [porting guide](#porting-an-opencode-config)) |
|
||||
| Cross-cutting `path` gate | No equivalent — `.env` protection is per-tool only | `path` surface denies/asks across all tools and bash at once; a `path` deny cannot be overridden by a per-tool allow |
|
||||
| OpenCode-only surfaces | `lsp`, `question`, `webfetch`, `websearch`, `todowrite`, `doom_loop` | Not applicable — Pi does not expose these tools or events |
|
||||
| File mutation surfaces | `edit` covers `edit`, `write`, `apply_patch` | Separate `write` and `edit` surfaces |
|
||||
| Search/discovery surfaces | `glob`, `grep`, `list` | `find`, `grep`, `ls` (Pi tool names) |
|
||||
| `mcp` surface | Not a documented permission surface | First-class with server/tool-level granularity |
|
||||
| Top-level string shorthand | `"permission": "allow"` sets all surfaces | Not supported; must use an object |
|
||||
| Per-agent config location | `agent` key in config JSON or YAML frontmatter | YAML frontmatter in agent `.md` files only |
|
||||
| Config file paths | `~/.config/opencode/opencode.json` | `~/.pi/agent/extensions/pi-permission-system/config.json` |
|
||||
| Subagent prompt forwarding | Not documented | `ask` policies work in non-UI subagent contexts |
|
||||
| Infrastructure auto-allow | N/A | Read-only tools to Pi infra dirs bypass the gate |
|
||||
| Permission review log | No equivalent documented | Writes decisions to a JSONL audit log |
|
||||
|
||||
### Notable Differences Explained
|
||||
|
||||
#### Default Fallback: `allow` vs `ask`
|
||||
|
||||
OpenCode defaults to permissive — most tools work without configuration.
|
||||
This extension defaults to least privilege — omitting `"*"` gives you `"ask"` for everything.
|
||||
|
||||
If you want OpenCode-like permissiveness:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"permission": {
|
||||
"*": "allow",
|
||||
"external_directory": "ask"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### File Mutation Surfaces
|
||||
|
||||
OpenCode unifies all file writes under a single `edit` permission.
|
||||
This extension exposes Pi's actual tool names: `write` (create/overwrite) and `edit` (targeted replacement).
|
||||
|
||||
To replicate OpenCode's unified behavior, set both to the same action:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"permission": {
|
||||
"write": "ask",
|
||||
"edit": "ask"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### MCP Surface (Pi-Only)
|
||||
|
||||
This extension provides a first-class `mcp` permission surface with granular server and tool-level control:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"permission": {
|
||||
"mcp": {
|
||||
"*": "ask",
|
||||
"mcp_status": "allow",
|
||||
"myServer:*": "ask",
|
||||
"dangerousServer": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
OpenCode does not expose MCP as a configurable permission surface.
|
||||
|
||||
#### Bash Path Extraction
|
||||
|
||||
Both systems use `web-tree-sitter` + `tree-sitter-bash` to parse shell commands into an AST for `external_directory` path detection, but the extraction strategies differ significantly:
|
||||
|
||||
**OpenCode** only extracts paths from a hardcoded allowlist of file-manipulating commands (`rm`, `cp`, `mv`, `mkdir`, `touch`, `chmod`, `chown`, `cat`, plus PowerShell equivalents).
|
||||
Commands not in the list — including `sed`, `awk`, `grep` — get no path extraction at all.
|
||||
For allowlisted commands, all non-flag positional arguments are assumed to be paths.
|
||||
|
||||
**This extension** extracts path candidates from all commands generically, then applies additional intelligence:
|
||||
|
||||
- A `PATTERN_FIRST_COMMANDS` map understands flag arity for `sed`, `awk`, `grep`, `rg`, and similar tools, distinguishing inline patterns/scripts from file arguments to avoid false positives.
|
||||
- Redirect destinations (`> /path/to/file`) are extracted.
|
||||
- Heredoc bodies, comments, and variable assignments are skipped.
|
||||
|
||||
The result is broader coverage (paths detected in any command, not just a curated list) with fewer false positives on pattern-first commands (no spurious prompts for sed regexes or grep patterns that happen to contain `/`).
|
||||
|
||||
## Porting an OpenCode Config
|
||||
|
||||
### Before (OpenCode)
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"permission": {
|
||||
"*": "allow",
|
||||
"bash": {
|
||||
"*": "ask",
|
||||
"git *": "allow",
|
||||
"npm *": "allow",
|
||||
"rm *": "deny"
|
||||
},
|
||||
"edit": {
|
||||
"*": "ask",
|
||||
"src/*.ts": "allow"
|
||||
},
|
||||
"external_directory": {
|
||||
"~/projects/*": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### After (this extension)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/gotgenes/pi-packages/main/packages/pi-permission-system/schemas/permissions.schema.json",
|
||||
"permission": {
|
||||
"*": "allow",
|
||||
"bash": {
|
||||
"*": "ask",
|
||||
"git *": "allow",
|
||||
"npm *": "allow",
|
||||
"rm *": "deny"
|
||||
},
|
||||
"write": "ask",
|
||||
"edit": "ask",
|
||||
"external_directory": {
|
||||
"*": "ask",
|
||||
"~/projects/*": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Translation Steps
|
||||
|
||||
1. **Replace `"permission": "allow"`** (top-level string) with `"permission": { "*": "allow" }`.
|
||||
2. **Split `edit`** into separate `write` and `edit` entries if you need different policies for create vs. modify.
|
||||
If not, set both to the same action.
|
||||
3. **Rename search surfaces**: `glob` → `find`, `list` → `ls`.
|
||||
4. **Add `.env` rules manually** if you relied on OpenCode's built-in protection.
|
||||
The `path` surface is the recommended approach — it covers all tools and bash in one rule:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"permission": {
|
||||
"path": {
|
||||
"*": "allow",
|
||||
"*.env": "deny",
|
||||
"*.env.*": "deny",
|
||||
"*.env.example": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, use per-tool patterns if you only need to protect specific tools (e.g., `read`):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"permission": {
|
||||
"read": {
|
||||
"*": "allow",
|
||||
"*.env": "deny",
|
||||
"*.env.*": "deny",
|
||||
"*.env.example": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Remove OpenCode-only surfaces** (`lsp`, `question`, `webfetch`, `websearch`, `todowrite`, `doom_loop`) — they have no effect in this extension.
|
||||
6. **Add `mcp` rules** if you use MCP servers — OpenCode has no equivalent, so this is new configuration.
|
||||
Reference in New Issue
Block a user