@czottmann/pi-automode 1.11.0 → 1.12.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 +24 -0
- package/README.md +78 -112
- package/docs/GLOSSARY.md +18 -14
- package/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +44 -0
- package/docs/adr/INDEX.md +5 -0
- package/docs/automode-classifier-flow.md +198 -104
- package/docs/configuration.md +155 -0
- package/docs/defaults.md +55 -14
- package/docs/diagnostics.md +90 -0
- package/docs/observability-logging.md +61 -26
- package/examples/automode.local.json +5 -0
- package/extensions/auto-mode/classifier.ts +172 -18
- package/extensions/auto-mode/config.ts +127 -30
- package/extensions/auto-mode/constants.ts +3 -0
- package/extensions/auto-mode/extension.ts +273 -31
- package/extensions/auto-mode/log.ts +60 -5
- package/extensions/auto-mode/paths.ts +92 -8
- package/extensions/auto-mode/permissions.ts +203 -13
- package/extensions/auto-mode/state.ts +1 -0
- package/extensions/auto-mode/types.ts +11 -0
- package/extensions/auto-mode/utils.ts +9 -1
- package/package.json +9 -2
- package/skills/automode-diagnostics/SKILL.md +63 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.12.0] - 2026-08-23
|
|
6
|
+
|
|
7
|
+
## New features
|
|
8
|
+
|
|
9
|
+
- **Deterministic permission allows** — Add user-owned `permissions.allow` patterns that skip classifier review after deterministic checks pass. Accepted ask rules still require classifier review. Thank you, @sergeykonkin! (#14)
|
|
10
|
+
- **Configurable classifier request timeout** — new `autoMode.classifierTimeoutMs` setting. The default is 20000 ms. The timeout applies to each classifier request. The fast stage and the detailed stage each have their own budget. A request that exceeds the timeout is aborted. Auto mode fails closed and blocks the action.
|
|
11
|
+
- **Read-only agent diagnostics** — Add `automode_inspect` tool for status, configuration, defaults, and recent denial metadata. Thank you, @blalor! (#11)
|
|
12
|
+
|
|
13
|
+
## Bug fixes
|
|
14
|
+
|
|
15
|
+
- **Reject invalid config values** — Invalid boolean and log config values (e.g. `enabled: 0`, `log.enabled: 1`) are now rejected at merge time instead of being applied with diagnostics only. (#20)
|
|
16
|
+
- **Preserve defaults for malformed rule lists** — A malformed `hard_deny` entry like `[42]` no longer strips all built-in hard-deny rules. Malformed entries are rejected and defaults preserved conservatively. (#21)
|
|
17
|
+
- **Runtime classifier providers** — Dispatch classifier calls through Pi's runtime model registry so providers registered with `pi.registerProvider()` work immediately. Preserve normalized reasoning and header-only authentication on the temporary simple-completion bridge. (#15)
|
|
18
|
+
- **Bounded wildcard matching** — Replace regex-based permission and denied-path globs with a linear-time matcher. Reject oversized patterns and fail closed for oversized runtime inputs. (#19)
|
|
19
|
+
- **Complete classifier action input** — Send the exact current tool input to both classifier stages in a dedicated message. Block the action if it cannot fit without truncation. (#17)
|
|
20
|
+
- **Path policy normalization** — Use Pi-compatible resolution for file-tool paths, including file URLs, `@` and tilde aliases, and read fallback names. Enforce denied paths across omitted and recursive search scopes and both sides of symlink aliases. (#18)
|
|
21
|
+
- **Project config trust gate** — Ignore `.pi/automode.local.json` and `.pi/automode.json` until Pi trusts the project. Apply the trust gate during startup and config reloads. (#16)
|
|
22
|
+
- **In-memory observability logs** — Write logs to an extension-owned directory (`~/.pi/agent/extensions/pi-automode/logs/`) instead of the launching project directory. Thanks, @HerbertGao! (#13)
|
|
23
|
+
|
|
24
|
+
[1.12.0]: https://github.com/czottmann/pi-automode/compare/v1.11.0...v1.12.0
|
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@ Claude Code-style auto mode for Pi.
|
|
|
4
4
|
|
|
5
5
|
This is a guardrail extension. It intercepts agent tool calls before execution and blocks actions that match permission deny rules, deterministic hard-deny checks, or the auto-mode classifier's block decision.
|
|
6
6
|
|
|
7
|
-
It is not a sandbox. Extensions run in the Pi process
|
|
7
|
+
It is not a sandbox. Extensions run in the Pi process. A malicious extension can do anything that your user account can do.
|
|
8
|
+
|
|
9
|
+
Pi-automode does not guard user `!` or `!!` shell commands. It guards only agent tool calls. Use it to reduce unsafe autonomous tool use. Do not use it as an OS security boundary.
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
@@ -43,128 +45,53 @@ pi -e ./extensions/auto-mode.ts
|
|
|
43
45
|
|
|
44
46
|
`/auto-mode` is an alias.
|
|
45
47
|
|
|
46
|
-
##
|
|
48
|
+
## Agent diagnostics
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
The package registers one model-callable, read-only tool:
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
AM● a:12 d:2 ca:5 cd:1
|
|
52
|
-
```
|
|
52
|
+
`automode_inspect` accepts one `action`:
|
|
53
53
|
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
56
|
-
- `
|
|
57
|
-
- `
|
|
54
|
+
- `status`: active state and counters
|
|
55
|
+
- `config`: active effective configuration, log path, and diagnostics
|
|
56
|
+
- `defaults`: built-in rule lists
|
|
57
|
+
- `denials`: recent denial timestamps, kinds, and tool names
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
The tool reads the same in-memory configuration and state that the guardrail enforces. Permission and deterministic checks run before the bypass.
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
- [Auto-mode classifier flow](docs/automode-classifier-flow.md)
|
|
63
|
-
- [Observability logging](docs/observability-logging.md)
|
|
61
|
+
The bypass does not change automode counters, persisted state, or observability logs. The extension verifies the source of the tool before it applies the exemption. Thus, a name collision does not exempt a tool from another extension.
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
The tool cannot enable or disable auto mode. It cannot reload configuration, reset state, select a model, or change configuration.
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
Pi sends tool output to the current model. The `status` and `denials` views omit denial reasons and action payloads.
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
If a diagnosis requires a reason, inspect a known-safe entry in the local observability log. The `config` view includes effective rule text. It removes raw JSON parser details from diagnostics.
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
- `.pi/automode.local.json`
|
|
73
|
-
- `PI_AUTOMODE_SETTINGS_JSON`
|
|
69
|
+
Do not put credentials or other secrets in automode rules.
|
|
74
70
|
|
|
75
|
-
|
|
71
|
+
The bundled `automode-diagnostics` skill uses this tool to diagnose unexpected decisions without asking the user to copy output from slash commands. Configuration edits and automode state changes remain user-controlled. See [Agent diagnostics](docs/diagnostics.md) for the inspection contract, privacy limits, and diagnosis workflow.
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"autoMode": {
|
|
82
|
-
"enabled": false
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
This is project-local and should not be committed. Shared project `.pi/automode.json` cannot disable auto-mode.
|
|
88
|
-
|
|
89
|
-
Set a global default classifier model in `~/.pi/agent/automode.json`; override it per project in `.pi/automode.local.json`.
|
|
90
|
-
|
|
91
|
-
`classifierReasoningLevel` optionally requests `low`, `medium`, `high`, `xhigh`, or `max` reasoning for both classifier stages. If the key is absent, pi-automode sends no reasoning preference and leaves the choice to the server. Pi AI clamps unsupported values to the nearest level supported by the selected model; a non-reasoning model resolves to `off`. `low` matches Codex Auto Review's reasoning effort and the practical default when an explicit value is needed. Higher levels can consume the existing 512/1200-token stage limits before producing visible output, which causes the classifier to fail closed. Raise `fastClassifierMaxTokens` (default 512, integer ≥ 16) if you run a reasoning model whose fast-stage budget is truncated before it emits the required `0`/`1` digit.
|
|
92
|
-
|
|
93
|
-
`allowInsideWorkingDirectory` (default `false`) adds a deterministic silent-allow tier for the file tools (`read`, `write`, `edit`, `grep`, `find`, `ls`): when `true`, a call whose resolved path is inside the working directory is allowed without any classifier call, and file access outside the working directory is routed to the classifier (including reads, which would otherwise take the read-only fast path). This matches the Codex/Claude Code "inside the sandbox = silent, outside = review" model. The tier takes precedence over `classifyReadOnlyTools`: with both enabled, in-tree file access is still allowed without a classifier call, and out-of-tree file access is classified. `classifyReadOnlyTools: true` only routes in-tree reads to the classifier when `allowInsideWorkingDirectory` is `false`. Writes and edits to protected in-tree paths (`.git/hooks`, `.pi` controls, shell profiles, config files) are exempt from the silent-allow tier and still go to the classifier.
|
|
94
|
-
|
|
95
|
-
`deniedPaths` (default `[]`) is a list of path glob patterns that are hard-denied before the classifier and before the inside-working-directory tier — the file-tool equivalent of a secret/system deny list. Patterns support `~`, `$HOME`, and `${HOME}` expansion and `*` (which matches any characters, including `/`, so `**/id_rsa` matches a private key at any depth). Matching checks both the path as typed and its symlink-resolved form, so a `~/.ssh/*` rule still matches when `~/.ssh` is a symlink. A matching path blocks the call unconditionally (no classifier, no override). The deny list applies to file tools only; `bash` path access is governed by the classifier. Both keys follow the normal scalar/array precedence.
|
|
96
|
-
|
|
97
|
-
The setting follows the normal scalar precedence: global, then project-local, then `PI_AUTOMODE_SETTINGS_JSON`. Shared project `.pi/automode.json` cannot set it. Omitting the key at a higher-precedence scope does not clear a lower-precedence value.
|
|
98
|
-
|
|
99
|
-
Example:
|
|
100
|
-
|
|
101
|
-
```json
|
|
102
|
-
{
|
|
103
|
-
"autoMode": {
|
|
104
|
-
"classifierModel": "provider/model-id",
|
|
105
|
-
"classifierReasoningLevel": "low",
|
|
106
|
-
"classifyReadOnlyTools": false,
|
|
107
|
-
"fastClassifierMaxTokens": 512,
|
|
108
|
-
"allowInsideWorkingDirectory": false,
|
|
109
|
-
"deniedPaths": [],
|
|
110
|
-
"maxUserTranscriptTokens": 4000,
|
|
111
|
-
"maxToolTranscriptTokens": 4000,
|
|
112
|
-
"environment": [
|
|
113
|
-
"$defaults",
|
|
114
|
-
"Source control: github.example.com/acme-corp and all repos under it",
|
|
115
|
-
"Trusted internal domains: *.corp.example.com, git.example.com",
|
|
116
|
-
"Trusted cloud buckets: s3://acme-dev-artifacts, gs://acme-ci-cache",
|
|
117
|
-
"Key internal services: staging deploy API at deploy.corp.example.com"
|
|
118
|
-
],
|
|
119
|
-
"allow": ["$defaults"],
|
|
120
|
-
"protectedPaths": ["$defaults"],
|
|
121
|
-
"soft_deny": ["$defaults"],
|
|
122
|
-
"hard_deny": [
|
|
123
|
-
"$defaults",
|
|
124
|
-
"Never send repository contents to third-party code-review APIs"
|
|
125
|
-
]
|
|
126
|
-
},
|
|
127
|
-
"permissions": {
|
|
128
|
-
"deny": ["bash(rm -rf *)"],
|
|
129
|
-
"ask": ["bash(git push *)"]
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
`maxUserTranscriptTokens` and `maxToolTranscriptTokens` are approximate per-category budgets; both default to 4000 and accept integers of at least 32. The former `maxTranscriptLines` setting is no longer supported because evidence selection is token-budgeted rather than line-based.
|
|
135
|
-
|
|
136
|
-
### Ask-user tools and explicit authorization
|
|
137
|
-
|
|
138
|
-
Classifier evidence includes normal user messages and assistant tool-call inputs, but excludes assistant prose and all tool results. This includes answers returned by ask-user tools such as `@vanillagreen/pi-questions`. Selecting "Yes" there helps the agent decide what to do next, but pi-automode does not treat that tool result as explicit authorization to override a soft deny. Send the authorization as a normal chat message instead; the agent can then retry the action. Tool results are excluded because they may contain untrusted or prompt-injected content.
|
|
139
|
-
|
|
140
|
-
### `$defaults`
|
|
141
|
-
|
|
142
|
-
See [Defaults and rule-list behavior](docs/defaults.md) for built-in `environment`, `allow`, `protectedPaths`, `soft_deny`, and `hard_deny` entries, plus replacement behavior when `$defaults` is omitted.
|
|
143
|
-
|
|
144
|
-
### Observability logging
|
|
73
|
+
## Status line
|
|
145
74
|
|
|
146
|
-
|
|
75
|
+
When the Pi TUI is available, the extension renders a persistent status line:
|
|
147
76
|
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
"autoMode": {
|
|
151
|
-
"log": {
|
|
152
|
-
"enabled": true,
|
|
153
|
-
"classifierIo": false
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
77
|
+
```text
|
|
78
|
+
AM● a:12 d:2 ca:5 cd:1
|
|
157
79
|
```
|
|
158
80
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
81
|
+
- `AM` — auto-mode prefix. `●` means enabled. `○` means disabled through configuration or `/automode off`.
|
|
82
|
+
- `a:` — actions allowed so far (checked minus denied).
|
|
83
|
+
- `d:` — actions denied so far, for any reason (permission rule, deterministic hard-deny, or classifier).
|
|
84
|
+
- `ca:` / `cd:` — classifier decisions split into allowed and denied. These segments appear after the first classifier call. `d:` counts all denials, so `d:` is always `>= cd:`.
|
|
162
85
|
|
|
163
|
-
|
|
86
|
+
## Docs
|
|
164
87
|
|
|
165
|
-
|
|
88
|
+
- [Configuration](docs/configuration.md)
|
|
89
|
+
- [Defaults and rule-list behavior](docs/defaults.md)
|
|
90
|
+
- [Auto-mode classifier flow](docs/automode-classifier-flow.md)
|
|
91
|
+
- [Observability logging](docs/observability-logging.md)
|
|
92
|
+
- [Architecture decisions](docs/adr/INDEX.md)
|
|
166
93
|
|
|
167
|
-
## What
|
|
94
|
+
## What runs before the classifier
|
|
168
95
|
|
|
169
96
|
The extension blocks these before any allow or classifier decision:
|
|
170
97
|
|
|
@@ -177,11 +104,25 @@ The extension blocks these before any allow or classifier decision:
|
|
|
177
104
|
- root, home, and system-path destructive deletes
|
|
178
105
|
- edits to `.pi/automode*`, `.pi` auto-mode files, and this extension's safety-control files
|
|
179
106
|
|
|
180
|
-
|
|
107
|
+
After these checks, pi-automode applies `permissions.allow`. Protected `write` and `edit` targets continue to the classifier.
|
|
108
|
+
|
|
109
|
+
Accepted ask rules also continue to the classifier. They cannot use an allow tier. Pi-automode then allows the read-only tools `read`, `grep`, `find`, and `ls`. Every remaining action goes to the classifier.
|
|
110
|
+
|
|
111
|
+
A `permissions.allow` rule intentionally skips classifier policy, including classifier `hard_deny` rules. Use narrow patterns and user-owned configuration. Deterministic hard-deny and path controls remain unconditional.
|
|
181
112
|
|
|
182
|
-
|
|
113
|
+
Set `classifyReadOnlyTools: true` (default `false`) to route read-only tools through the classifier. This configuration value increases model calls, latency, and session cost.
|
|
183
114
|
|
|
184
|
-
|
|
115
|
+
Pi-automode blocks a `deniedPaths` match before classifier review or an allow tier. Thus, file tools cannot send matching secret or system paths to the model.
|
|
116
|
+
|
|
117
|
+
The list does not govern `bash`. The classifier and deterministic hard-deny checks govern shell access to these paths.
|
|
118
|
+
|
|
119
|
+
The value `allowInsideWorkingDirectory: true` allows file access inside the working directory locally. Pi-automode sends all outside file access to the classifier, including reads.
|
|
120
|
+
|
|
121
|
+
Classification starts with a conservative one-token filter. If the filter requests review, pi-automode runs structured review.
|
|
122
|
+
|
|
123
|
+
Both stages receive the complete current tool input in a dedicated message. Transcript truncation cannot remove action content. If the exact input cannot fit in the classifier context, auto mode blocks the call.
|
|
124
|
+
|
|
125
|
+
Both stages use a classifier-specific session key. They request short cache retention from providers that support it. A missing model, provider failure, or malformed response blocks the action.
|
|
185
126
|
|
|
186
127
|
## Examples
|
|
187
128
|
|
|
@@ -199,19 +140,44 @@ npm test
|
|
|
199
140
|
npm pack --dry-run
|
|
200
141
|
```
|
|
201
142
|
|
|
202
|
-
The tests cover
|
|
143
|
+
The tests cover these safety-sensitive areas:
|
|
144
|
+
|
|
145
|
+
- scoped permission matching
|
|
146
|
+
- the `permissions.allow` tier and its precedence
|
|
147
|
+
- configuration-source precedence and diagnostics
|
|
148
|
+
- `$defaults` behavior
|
|
149
|
+
- deterministic hard-deny checks and shell parsing
|
|
150
|
+
- classifier routing for `write` and `edit`
|
|
151
|
+
- symlink-aware safety-control checks
|
|
152
|
+
- token-budgeted transcript selection
|
|
153
|
+
- staged classifier parsing and cache behavior
|
|
154
|
+
- hook-level allow and block behavior
|
|
203
155
|
|
|
204
156
|
## Publishing
|
|
205
157
|
|
|
206
|
-
GitHub Actions publishes the package to npm
|
|
158
|
+
When a maintainer publishes a GitHub Release, GitHub Actions publishes the package to npm. The release tag must match `package.json` exactly. The forms `v1.0.0` and `1.0.0` both match version `1.0.0`.
|
|
207
159
|
|
|
208
160
|
The workflow uses npm Trusted Publishing, so it does not need an npm token secret. Configure this package on npm with this repository and workflow file (`.github/workflows/publish.yml`). The workflow builds the package, runs `npm run check`, and publishes with npm provenance.
|
|
209
161
|
|
|
210
162
|
### Release tag must point at the version bump
|
|
211
163
|
|
|
212
|
-
The publish workflow checks
|
|
164
|
+
The publish workflow checks the commit that the release tag identifies. It compares the version in `package.json` with the tag name.
|
|
165
|
+
|
|
166
|
+
**The tag must identify a commit that contains the new version.**
|
|
167
|
+
|
|
168
|
+
1. Commit the version change with `chore: release X.Y.Z`.
|
|
169
|
+
2. Push `main`.
|
|
170
|
+
3. Create the GitHub release from that commit.
|
|
171
|
+
|
|
172
|
+
If the tag identifies an older version, the `Check release tag` step fails. It reports `Release tag (vX.Y.Z) does not match package.json version (x.y.z)`.
|
|
173
|
+
|
|
174
|
+
CAUTION: Do not move a correct release tag. Moving the tag changes a published reference.
|
|
175
|
+
|
|
176
|
+
1. If the tag identifies the wrong commit, force-move it to the version-change commit.
|
|
177
|
+
2. Force-push the corrected tag.
|
|
178
|
+
3. Run `gh workflow run publish.yml --ref vX.Y.Z`.
|
|
213
179
|
|
|
214
|
-
|
|
180
|
+
When GitHub creates the tag, the `release` event occurs. A rerun of a failed `release` workflow uses the original reference. It does not use the moved tag.
|
|
215
181
|
|
|
216
182
|
## Author
|
|
217
183
|
|
package/docs/GLOSSARY.md
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
# Glossary
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This glossary defines project terms for `@czottmann/pi-automode`. It does not define standard technical words. Each entry links to a longer explanation where applicable.
|
|
4
4
|
|
|
5
5
|
## Enforcement flow
|
|
6
6
|
|
|
7
|
-
The ordered pipeline that runs
|
|
7
|
+
The enforcement flow is the ordered pipeline that runs before each agent tool call. See [Auto-mode classifier flow](automode-classifier-flow.md).
|
|
8
8
|
|
|
9
|
-
**Auto mode** — Claude Code-style guardrail posture
|
|
9
|
+
**Auto mode** — A Claude Code-style guardrail posture. A pre-execution classifier allows routine, reversible actions and blocks risky actions. It replaces routine permission prompts.
|
|
10
10
|
|
|
11
|
-
**Deterministic hard-deny** — Local code checks that block high-risk actions before
|
|
11
|
+
**Deterministic hard-deny** — Local code checks that block high-risk actions before classifier review. A user or model cannot override them. They are independent of the classifier-level [hard_deny](#classifier-policy-and-rules) rules.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
**`permissions.allow` tier** — A user-owned list of scoped tool patterns. A match skips classifier review after deterministic checks pass. An accepted `permissions.ask` rule disables this tier for the current call. The tier cannot override a denial or cover protected `write` and `edit` targets. See [ADR-001](adr/ADR-001-permission-precedence-and-trust-boundaries.md).
|
|
14
14
|
|
|
15
|
-
**
|
|
15
|
+
**Read-only bypass** — The default allow tier for `read`, `grep`, `find`, and `ls`. Permission and deterministic checks run before this tier. `classifyReadOnlyTools` sends these calls to the classifier instead.
|
|
16
16
|
|
|
17
|
-
**
|
|
17
|
+
**Staged classifier** — A two-stage safety classifier. A conservative one-token filter controls access to an optional structured review. See [Fast stage](#enforcement-flow) and [Detailed stage](#enforcement-flow).
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**Fast stage** — The first classifier stage. It returns `0` for a clearly allowed action or `1` for an action that can require review.
|
|
20
|
+
|
|
21
|
+
**Detailed stage** — The second classifier stage. It runs after a fast-stage review result and returns a structured allow or block decision.
|
|
20
22
|
|
|
21
23
|
## Classifier policy and rules
|
|
22
24
|
|
|
23
|
-
The classifier
|
|
25
|
+
The classifier policy defines denial tiers and rule-list syntax. See [Defaults and rule-list behavior](defaults.md).
|
|
26
|
+
|
|
27
|
+
**hard_deny** — Classifier rules that block unconditionally. They are independent of the code-level [deterministic hard-deny](#enforcement-flow) checks.
|
|
24
28
|
|
|
25
|
-
**
|
|
29
|
+
**soft_deny** — Classifier rules that normally block but support defined overrides. Unlike [hard_deny](#classifier-policy-and-rules), these rules are not unconditional.
|
|
26
30
|
|
|
27
|
-
**
|
|
31
|
+
**explicit_intent** — A classifier tier for direct user authorization. The latest user instruction specifically authorizes an action that matches a [soft_deny](#classifier-policy-and-rules) rule.
|
|
28
32
|
|
|
29
|
-
**
|
|
33
|
+
**allow exception** (`autoMode.allow`) — A prose rule that overrides a matching [soft_deny](#classifier-policy-and-rules) rule. It cannot override [hard_deny](#classifier-policy-and-rules). It is independent of the [`permissions.allow` tier](#enforcement-flow).
|
|
30
34
|
|
|
31
|
-
**`$defaults`** — A section-local marker in a rule list
|
|
35
|
+
**`$defaults`** — A section-local marker in a rule list. It expands to the built-in entries for that list.
|
|
32
36
|
|
|
33
37
|
## Status
|
|
34
38
|
|
|
35
|
-
**AM status line** — The persistent TUI footer
|
|
39
|
+
**AM status line** — The persistent TUI footer that starts with `AM`. It reports the auto-mode state and action counts.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ADR-001: Permission precedence and configuration trust boundaries
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-23
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
Pi-automode supports `permissions.deny`, `permissions.ask`, and `permissions.allow` tool patterns. These rules affect whether a tool call reaches the classifier.
|
|
8
|
+
|
|
9
|
+
An allow rule reduces classifier coverage. A checked-in repository file must not reduce that coverage. An accepted ask rule must also keep its promise of classifier review.
|
|
10
|
+
|
|
11
|
+
The wildcard matcher limits its input size to keep matching time bounded. Deny and ask rules must fail closed for oversized inputs. The same behavior is unsafe for allow rules.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
Pi-automode uses this permission policy:
|
|
16
|
+
|
|
17
|
+
1. A matching `permissions.deny` rule blocks the tool call.
|
|
18
|
+
2. A matching `permissions.ask` rule requires user confirmation.
|
|
19
|
+
3. If the user declines, pi-automode blocks the tool call.
|
|
20
|
+
4. If the user accepts, deterministic denial checks continue.
|
|
21
|
+
5. After these checks pass, the classifier reviews the accepted tool call.
|
|
22
|
+
6. An accepted ask rule disables all later deterministic allow tiers for that call.
|
|
23
|
+
7. If no ask rule matched, a `permissions.allow` rule can skip classifier review.
|
|
24
|
+
8. Deterministic hard-deny checks, path-deny checks, and protected-path controls take precedence over allow rules.
|
|
25
|
+
|
|
26
|
+
Pi-automode reads `permissions.allow` only from user-owned configuration sources:
|
|
27
|
+
|
|
28
|
+
- `~/.pi/agent/automode.json`
|
|
29
|
+
- trusted `.pi/automode.local.json`
|
|
30
|
+
- `PI_AUTOMODE_SETTINGS_JSON`
|
|
31
|
+
|
|
32
|
+
Shared `.pi/automode.json` can add deny and ask rules after project trust. It cannot add allow rules.
|
|
33
|
+
|
|
34
|
+
For an oversized matcher input, deny and ask rules return a match. Allow rules return no match. This behavior keeps denial rules fail-closed without broadening allow rules.
|
|
35
|
+
|
|
36
|
+
## Consequences
|
|
37
|
+
|
|
38
|
+
A repository cannot use checked-in configuration to remove classifier review. Users can still add project-specific allow rules in the local configuration file.
|
|
39
|
+
|
|
40
|
+
An accepted ask rule always causes classifier review after deterministic checks pass. This behavior is stricter than a model where confirmation grants direct permission.
|
|
41
|
+
|
|
42
|
+
The matcher needs separate overflow behavior for denial and allow contexts. Tests must cover both behaviors.
|
|
43
|
+
|
|
44
|
+
The permission lists are not symmetric across configuration sources. The documentation and diagnostics must make this difference clear.
|