@erichll/pi-auto-review 0.15.1 → 0.15.3
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 +20 -305
- package/README.md +8 -1
- package/package.json +3 -3
- package/src/policy-audit/classifier.ts +60 -3
- package/src/policy.ts +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,307 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.15.
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
estimate for CJK text, so multi-kilobyte Chinese payloads (e.g. a long plan
|
|
24
|
-
document submitted through a tool call) synchronously failed the
|
|
25
|
-
`reviewer_input_budget_exceeded` preflight before the reviewer model was
|
|
26
|
-
ever called, and the request was denied as "Automatic review is
|
|
27
|
-
unavailable." CJK code points now estimate at one token each and remaining
|
|
28
|
-
code points at their UTF-8 byte length over three (estimator
|
|
29
|
-
`conservative:cjk-aware`).
|
|
30
|
-
- Record the denial when a reviewer-unavailable review fails closed, so
|
|
31
|
-
`/auto-review-approve` — the recovery command that verdict recommends —
|
|
32
|
-
actually has a matching denial to approve. Previously the recommendation
|
|
33
|
-
was dead text for this failure class.
|
|
34
|
-
- Let an explicit human override (`/auto-review-approve` trusted exact retry)
|
|
35
|
-
bypass the reviewer input-budget preflight failure. A sizing estimate is
|
|
36
|
-
not a safety verdict and must not veto an already-authorized retry; the
|
|
37
|
-
review proceeds with truncated evidence.
|
|
38
|
-
- Show "waiting for you" in the review widget while a `ctx.ui` prompt blocks
|
|
39
|
-
the session during an active review (pi >= 0.84.4 `ui_prompt_start` /
|
|
40
|
-
`ui_prompt_end`). The widget previously kept claiming "Waiting for
|
|
41
|
-
<model>…" while pi was actually waiting on the user (permission dialog,
|
|
42
|
-
`/review-denials`, break-glass challenge). Completed outcome summaries and
|
|
43
|
-
sessions without a live widget are unaffected.
|
|
44
|
-
- Fix `/auto-review-approve` and `/auto-review-break-glass` never applying in
|
|
45
|
-
real TUI sessions. The exact-match request hash included `toolCallId`, but a
|
|
46
|
-
retry is always a brand-new model tool call with a fresh tool call ID, so
|
|
47
|
-
the authorization could never match the retried request. The hash now
|
|
48
|
-
excludes both retry-minted identifiers (`id` was already excluded).
|
|
49
|
-
- Fix break-glass authorization being discarded on every retry. The stored
|
|
50
|
-
authorization was bound to the denial-time turn scope, but the command flow
|
|
51
|
-
appends a user message (`pi.sendUserMessage`) before the agent retries, so
|
|
52
|
-
the retry always lands in a later turn whose scope differs. Consumption now
|
|
53
|
-
binds on session and request hash plus the confirmation TTL instead of the
|
|
54
|
-
turn scope. The non-critical approve path already behaved this way.
|
|
55
|
-
- Reset the turn circuit breaker when a break-glass allow is consumed, so the
|
|
56
|
-
human-authorized scope can continue instead of staying blocked for the rest
|
|
57
|
-
of the turn. Fresh repeated denials still trip it again after three
|
|
58
|
-
consecutive failures.
|
|
59
|
-
- Clean up break-glass authorization bookkeeping: an expired authorization
|
|
60
|
-
that was never consumed no longer blocks re-approving the same action, and
|
|
61
|
-
a fresh denial of an action whose approval was already consumed re-arms
|
|
62
|
-
one-shot approval (one approval per denial).
|
|
63
|
-
|
|
64
|
-
## 0.14.0 - 2026-08-28
|
|
65
|
-
|
|
66
|
-
- Upgrade `/auto-review-policy-audit` from redacted statistics to an
|
|
67
|
-
evidence-driven permission-configuration optimizer. Reports now separate
|
|
68
|
-
suggested allow rules, keep-ask rules, and insufficient evidence, and emit a
|
|
69
|
-
copyable JSON fragment targeting the selected project or global
|
|
70
|
-
permission-system configuration. Suggestions remain advisory and never read
|
|
71
|
-
or modify permission configuration.
|
|
72
|
-
- Discover candidates from actual audit data without built-in tool, executable,
|
|
73
|
-
Git, RTK, package-manager, or read-only command catalogs. Valid observed
|
|
74
|
-
surfaces and sanitized Bash templates—including unknown tools and write
|
|
75
|
-
operations—can qualify after the configured minimum number of successful
|
|
76
|
-
ask-path approvals. Denials, gate failures, unavailable confirmations,
|
|
77
|
-
forwarded requests, and structurally unsafe Bash variants block a suggestion.
|
|
78
|
-
- Migrate the policy-audit database transactionally to schema v2. Existing
|
|
79
|
-
aggregate counts and `collecting_since` are preserved, while the new
|
|
80
|
-
`recommendations_since` boundary prevents pre-migration data from producing
|
|
81
|
-
allow suggestions. Valid custom tool names and sanitized Bash templates are
|
|
82
|
-
retained for reporting. Templates replace paths, URLs, assignments, quoted
|
|
83
|
-
values, and option values with wildcards; raw commands and non-Bash tool
|
|
84
|
-
arguments or inputs remain excluded.
|
|
85
|
-
|
|
86
|
-
## 0.13.0 - 2026-08-28
|
|
87
|
-
|
|
88
|
-
- Add compatibility with `@gotgenes/pi-permission-system` 27.1 directional
|
|
89
|
-
`path_*` and `external_directory_*` surfaces while retaining the original
|
|
90
|
-
surface in authorization evidence, UI binding, logs, and policy audits.
|
|
91
|
-
- Keep the permission-system peer range at `^27.0.0` so existing 27.x bare
|
|
92
|
-
`path` and `external_directory` integrations remain supported.
|
|
93
|
-
|
|
94
|
-
## 0.12.0 - 2026-08-27
|
|
95
|
-
|
|
96
|
-
- Add the private policy audit
|
|
97
|
-
compare current-project or anonymous global surface/command hotspots, denial
|
|
98
|
-
rates, approval sources, and rule-hit fingerprints; then review repeatedly
|
|
99
|
-
approved read-only operations as candidates for narrowly scoped `allow`
|
|
100
|
-
rules. Mutation, project execution, network, arbitrary-shell, unknown, and
|
|
101
|
-
custom-tool activity is called out as keep-`ask` evidence rather than an
|
|
102
|
-
automatic relaxation. The report does not edit policy or claim that an
|
|
103
|
-
unobserved rule is unused.
|
|
104
|
-
- Add default-on, 180-day permission-policy auditing backed by built-in
|
|
105
|
-
`node:sqlite`. Permission decisions are immediately reduced to fixed command,
|
|
106
|
-
risk, path-class, source, and anonymous rule-hit aggregates; raw commands,
|
|
107
|
-
paths, URLs, credentials, request IDs, and project names are never stored.
|
|
108
|
-
- Add `/auto-review-policy-audit` as an extension-owned custom-entry report.
|
|
109
|
-
It is not registered as an Agent tool or packaged skill, so audit reporting
|
|
110
|
-
adds no tool schema or skill metadata to the model context. Reports default
|
|
111
|
-
to the current project and can explicitly show an anonymous global aggregate.
|
|
112
|
-
- Require Node.js 22.13.0 or newer. No SQLite CLI, system library, npm SQLite
|
|
113
|
-
package, RTK, or historical permission-log import is required.
|
|
114
|
-
|
|
115
|
-
## 0.11.1 - 2026-08-25
|
|
116
|
-
|
|
117
|
-
- Align the package version with `@erichll/pi-sandbox` `0.11.1`. No
|
|
118
|
-
pi-auto-review runtime or public API changes.
|
|
119
|
-
|
|
120
|
-
## 0.11.0 - 2026-08-24
|
|
121
|
-
|
|
122
|
-
- **Structured network destination.** Network approval requests now carry
|
|
123
|
-
separate host, port, and protocol fields alongside the existing combined
|
|
124
|
-
destination string. One-shot grants bind each component independently, so a
|
|
125
|
-
grant issued for one host-port-protocol combination cannot be reused for a
|
|
126
|
-
different one.
|
|
127
|
-
- **Deferred reviews are more visible.** The review widget now highlights
|
|
128
|
-
deferred verdicts with a warning accent instead of the previous muted style,
|
|
129
|
-
making pending human decisions easier to spot.
|
|
130
|
-
|
|
131
|
-
## 0.10.0 - 2026-08-22
|
|
132
|
-
|
|
133
|
-
- Replace the review footer and newly appended transcript entries with one
|
|
134
|
-
above-editor `pi-auto-review` widget. Each permission check independently
|
|
135
|
-
replaces the previous display, shows the configured reviewer model while it
|
|
136
|
-
runs, and updates in place with its final outcome and metadata. The latest
|
|
137
|
-
result remains until the next check and is cleared on session change or
|
|
138
|
-
shutdown.
|
|
139
|
-
- Prevent late concurrent completions and permission decisions from overwriting
|
|
140
|
-
a newer check. A current local denial adds `Local confirmation · denied` to
|
|
141
|
-
that result only. Widget failures fall back to notifications without changing
|
|
142
|
-
authorization behavior.
|
|
143
|
-
- Remove tool-call grouping, timers, capacity eviction, and tool/turn flush
|
|
144
|
-
wiring for new feedback while retaining the historical entry renderer. Bound
|
|
145
|
-
target and rationale text with visible ellipses, preserve width-aware wrapping,
|
|
146
|
-
and keep token, duration, and call-count numbers in the normal metadata color.
|
|
147
|
-
|
|
148
|
-
## 0.9.0 - 2026-08-22
|
|
149
|
-
|
|
150
|
-
- Require `@gotgenes/pi-permission-system` `^27.0.0` and register through the
|
|
151
|
-
session-keyed `getPermissionsService(sessionId)` locator. Repeated ready
|
|
152
|
-
broadcasts are idempotent; session changes, shutdown, and late asynchronous
|
|
153
|
-
imports cannot retain or revive a stale authorizer registration. In-process
|
|
154
|
-
child nodes register against their own service without replacing the
|
|
155
|
-
parent's process-local broker capability.
|
|
156
|
-
- Group local TUI review results by `(sessionId, toolCallId)` into one
|
|
157
|
-
transcript entry while preserving independent asks, model calls, verdicts,
|
|
158
|
-
grants, confirmations, and audit records. Denials flush immediately; tool
|
|
159
|
-
start, terminal permission denial, turn/agent/session end, a 60-second TTL,
|
|
160
|
-
and bounded-capacity eviction provide lossless fallback flushes. Forwarded,
|
|
161
|
-
uncorrelated, and non-TUI reviews remain immediate.
|
|
162
|
-
- Add `toolCallId` to each `pi_auto_review_decision` audit entry and retain
|
|
163
|
-
compatibility rendering for historical single-result transcript entries.
|
|
164
|
-
- Remove the review entry's left quote bar and combine each target with its
|
|
165
|
-
rationale and member surface/outcome on one line, keeping model/token/duration
|
|
166
|
-
metadata separate.
|
|
167
|
-
- Label every displayed model token count with `toks` in both grouped and
|
|
168
|
-
single-result output.
|
|
169
|
-
|
|
170
|
-
## 0.8.2 - 2026-08-21
|
|
171
|
-
|
|
172
|
-
- Structure interactive review results as headline, target, rationale, and a
|
|
173
|
-
meta line with the reviewer model name, input/output tokens, duration, and extra
|
|
174
|
-
calls. The TUI renders them as markdown-style quote rows (`│` + `mdQuote`),
|
|
175
|
-
with allow/deny verbs and token counts tinted from the theme. The model id is
|
|
176
|
-
shown without a provider prefix; audit still records the full `provider/id`.
|
|
177
|
-
Local hard denies omit model and token fields because no model ran.
|
|
178
|
-
|
|
179
|
-
## 0.8.1 - 2026-08-21
|
|
180
|
-
|
|
181
|
-
- Restore TUI auto-confirm for capped `path` / `external_directory` allows on
|
|
182
|
-
permission-system 26.x. `permissions:ui_prompt` no longer carries `message`;
|
|
183
|
-
the bridge now binds `requestId` plus `request.value` (falling back to the
|
|
184
|
-
display `value`, then the legacy `message`) and fingerprints the live dialog
|
|
185
|
-
with those fields. Top-level display `surface` is not treated as the gate.
|
|
186
|
-
|
|
187
|
-
## 0.8.0 - 2026-08-21
|
|
188
|
-
|
|
189
|
-
- Add `/auto-review-approve` for exact non-critical denial retries and remove
|
|
190
|
-
the unprefixed `/approve` command without a compatibility alias.
|
|
191
|
-
- Add interactive `/auto-review-break-glass` for a recent critical model deny.
|
|
192
|
-
The high-friction confirmation is bound to the full request hash, session,
|
|
193
|
-
scope, and original request ID, expires after 60 seconds, permits one direct
|
|
194
|
-
retry, and never bypasses deterministic or protected-write hard denies.
|
|
195
|
-
- Add tighten-only `breakGlassEnabled` (default `true`), structured break-glass
|
|
196
|
-
allow provenance, and challenge/authorization/consumption/rejection audit
|
|
197
|
-
events. Challenge phrases are never included in audit events.
|
|
198
|
-
|
|
199
|
-
## 0.7.0 - 2026-08-20
|
|
200
|
-
|
|
201
|
-
- **Named `/home` paths are not a home wipe.** The reviewer prompt now matches
|
|
202
|
-
the deterministic hard-deny: recursive forced wipe of `/`, `~`, `$HOME`, or
|
|
203
|
-
the home directory itself remains forbidden. A narrow, user-requested delete
|
|
204
|
-
of named files or directories under `/home/...` is high-risk, not critical.
|
|
205
|
-
High risk without medium/high user authorization defers to the human instead
|
|
206
|
-
of denying as if it were a hard deny. The fixed prompt is compacted without
|
|
207
|
-
changing those rules.
|
|
208
|
-
- **Clearer agent-facing denials.** Authorizer denies still fail closed, but the
|
|
209
|
-
teaching reason now states that automatic policy denied the request (not a
|
|
210
|
-
human click), forbids rephrasing or circumvention, and points to the exact
|
|
211
|
-
retry command
|
|
212
|
-
for one exact retry when the user already requested that action. The
|
|
213
|
-
permission-system wrapper may still say `User denied`; the reason suffix is
|
|
214
|
-
the correction this package can own.
|
|
215
|
-
|
|
216
|
-
## 0.6.0 - 2026-08-20
|
|
217
|
-
|
|
218
|
-
- **Remove host user-constraint overlay.** Stop rewriting model allows from
|
|
219
|
-
regex matches on user text (`constraintEffect` / `vagueContinuation` /
|
|
220
|
-
authorization ceilings). Older messages enter evidence only via an exact
|
|
221
|
-
request reference or trusted exact retry. Compaction summaries remain
|
|
222
|
-
excluded from user intent. Hard denies, grant hashing, and the reviewer
|
|
223
|
-
model are unchanged.
|
|
224
|
-
|
|
225
|
-
## 0.5.0 - 2026-08-20
|
|
226
|
-
|
|
227
|
-
- **Safer evidence selection.** Review only request-linked tool calls, paired
|
|
228
|
-
results, current-task user intent, and bounded security evidence. Compaction
|
|
229
|
-
summaries cannot restore authorization, raw revocations override model
|
|
230
|
-
allows, and missing mandatory evidence fails closed before a model call.
|
|
231
|
-
- **Independent, smaller reviewer requests.** Use one compact canonical request
|
|
232
|
-
envelope and a byte-stable policy prefix, reducing fixed input from 2,011 to
|
|
233
|
-
1,396 characters. Independent SSE calls prevent prior review state from
|
|
234
|
-
carrying into later approvals while preserving cache/routing identity.
|
|
235
|
-
- **Explicit token limits.** Add tighten-only `maxReviewerInputTokens` with an
|
|
236
|
-
8,192 default and conservative UTF-8 accounting across the complete prompt.
|
|
237
|
-
Lower the default `maxTokens` from 1,600 to 256 after sequential real-model
|
|
238
|
-
validation at 384 and 256; the legal range remains 256–4,096.
|
|
239
|
-
- **Typed, bounded retries.** Share one deadline across each review, disable
|
|
240
|
-
provider-internal retries, and allow at most two actual model calls. Only
|
|
241
|
-
format errors, recognized connection/5xx failures, and bounded 429 responses
|
|
242
|
-
retry; deterministic and unknown failures remain terminal and fail closed.
|
|
243
|
-
- **Privacy-bounded telemetry.** Record per-attempt usage and per-review
|
|
244
|
-
summaries for both reviewer entry points using stable status/error codes and
|
|
245
|
-
prompt-part counts, without logging evidence text, provider errors,
|
|
246
|
-
credentials, headers, or URL query values.
|
|
247
|
-
|
|
248
|
-
## 0.4.0 - 2026-08-19
|
|
249
|
-
|
|
250
|
-
- **Credential-exfiltration hardening.** Expand deterministic terminal denies to cover
|
|
251
|
-
`.env` variants, common credential files, additional file readers and encoders,
|
|
252
|
-
shell substitutions, staged variables, redirects, and multi-stage pipelines that
|
|
253
|
-
upload credentials to network sinks. Template files such as `.env.example` and
|
|
254
|
-
`.env.sample` remain reviewable.
|
|
255
|
-
- **Reviewer refresh safety.** Re-resolve reviewer model/provider metadata for each
|
|
256
|
-
review, reacquire authentication for each attempt, and bind the reviewer session
|
|
257
|
-
identity to the endpoint and authentication fingerprint so provider or OAuth
|
|
258
|
-
refreshes cannot reuse a stale connection.
|
|
259
|
-
- **Compatibility.** Require Pi 0.84.1+ and permission-system 25.1.0+ to match the
|
|
260
|
-
current reviewer and forwarded-permission contracts.
|
|
261
|
-
|
|
262
|
-
## 0.3.5 - 2026-08-13
|
|
263
|
-
|
|
264
|
-
- Raise the default model-review `timeoutMs` from 45s to 90s and the default
|
|
265
|
-
`retries` from 1 to 2. Approval now tolerates real-world model latency better
|
|
266
|
-
while keeping the same bounded transcript surface.
|
|
267
|
-
|
|
268
|
-
## 0.3.4 - 2026-08-08
|
|
269
|
-
|
|
270
|
-
- Normalize direct and permission-system v24 forwarded evidence before policy
|
|
271
|
-
checks, model review, grant hashing, and audit logging. Forwarded Bash
|
|
272
|
-
commands, canonical path boundaries, agent names, and requester session IDs
|
|
273
|
-
now remain bound to the exact reviewed request.
|
|
274
|
-
- Add an opt-in `PI_AUTO_REVIEW_AUDIT_FILE` JSONL sink for isolated release
|
|
275
|
-
verification without changing the normal event-based audit surface.
|
|
276
|
-
|
|
277
|
-
## 0.3.3 - 2026-08-06
|
|
278
|
-
|
|
279
|
-
- Fix config validation rejecting multi-segment model ids (`provider/group/model`).
|
|
280
|
-
`validateConfig` no longer restricts a model to exactly one `/`; it only rejects
|
|
281
|
-
ids with empty segments. Segments are resolved as before: the first segment is
|
|
282
|
-
the provider and the rest is the model id.
|
|
283
|
-
|
|
284
|
-
## 0.3.2 - 2026-07-29
|
|
285
|
-
|
|
286
|
-
- Load an optional user-global trusted config from
|
|
287
|
-
`~/.pi/agent/extensions/pi-auto-review/config.json` as a full overlay on the
|
|
288
|
-
package defaults (model, `autoConfirmBoundedAllows`, and other legal keys).
|
|
289
|
-
- Keep project `.pi/pi-auto-review.json` tighten-only on top of that trusted
|
|
290
|
-
layer.
|
|
291
|
-
- Deterministically deny writes to the user-global reviewer config directory.
|
|
292
|
-
- Default reviewer model is the bare id `codex-auto-review`; `provider/model`
|
|
293
|
-
remains supported. Bare ids resolve against registered available models.
|
|
294
|
-
- Show user-facing review feedback in interactive UI: a footer status while the
|
|
295
|
-
model is reviewing, then a short toast for allow / deny / defer / auto-confirm
|
|
296
|
-
/ circuit-breaker outcomes (separate from agent denial reasons).
|
|
297
|
-
|
|
298
|
-
## 0.3.1 - 2026-07-29
|
|
299
|
-
|
|
300
|
-
- Bind one-shot approval grants to matched policy evidence as well as the
|
|
301
|
-
command, working directory, destination, paths, and other request fields.
|
|
302
|
-
- Prevent a grant issued for one Host-IPC trigger from authorizing a request
|
|
303
|
-
with different trigger evidence.
|
|
304
|
-
|
|
305
|
-
## 0.3.0 - 2026-07-28
|
|
306
|
-
|
|
307
|
-
- Initial public release.
|
|
3
|
+
## 0.15.3 - 2026-09-03
|
|
4
|
+
|
|
5
|
+
- Tolerate a single enclosing ```` ```json ```` or bare Markdown code fence
|
|
6
|
+
around reviewer decisions while preserving strict decision-schema
|
|
7
|
+
validation (fixes reviewer models that fence JSON despite the prompt,
|
|
8
|
+
notably when routed through Claude Code).
|
|
9
|
+
- Verify compatibility with `@gotgenes/pi-permission-system` 30.2.0 and
|
|
10
|
+
31.0.0, widen the peer range through 31.x, and move the development baseline
|
|
11
|
+
to 31.0.0.
|
|
12
|
+
- Keep permission-system 31 statement-operand audit classification aligned for
|
|
13
|
+
`for`/`select` word lists and `case` subjects without treating case patterns
|
|
14
|
+
as accessed paths.
|
|
15
|
+
- Confirm that model auto-confirm stays one-shot and cannot select
|
|
16
|
+
permission-system 30.2's wider both-directions session grant.
|
|
17
|
+
|
|
18
|
+
## 0.15.2 - 2026-09-02
|
|
19
|
+
|
|
20
|
+
- No behavior changes. Verified against `@gotgenes/pi-permission-system`
|
|
21
|
+
29.x with a development baseline of `29.3.0`; the peer range now accepts
|
|
22
|
+
29.x alongside 28.x.
|
package/README.md
CHANGED
|
@@ -31,7 +31,8 @@ that dependency is a hard prerequisite (see [Install and enable](#install-and-en
|
|
|
31
31
|
> **Prerequisite:** pi-auto-review is an authorizer inside
|
|
32
32
|
> `@gotgenes/pi-permission-system`. Pi does not auto-install peer packages, so
|
|
33
33
|
> install the permission system separately (once per machine) before this
|
|
34
|
-
> extension.
|
|
34
|
+
> extension. This release line supports permission-system 29.3.0 through the
|
|
35
|
+
> latest 31.x release:
|
|
35
36
|
|
|
36
37
|
Node.js 22.13.0 or newer is required. Permission auditing uses Node's built-in
|
|
37
38
|
`node:sqlite`; it does not require a SQLite CLI, system SQLite library, or npm
|
|
@@ -76,6 +77,12 @@ following recognized permission dialog. The bridge is request-ID-bound,
|
|
|
76
77
|
expires after ten seconds, and is consumed once. Mode, component, request, or
|
|
77
78
|
event-order mismatches leave the original human dialog in place.
|
|
78
79
|
|
|
80
|
+
Permission-system 31.x also projects paths named directly by `for`/`select`
|
|
81
|
+
word lists and `case` subjects onto the existing `path` and
|
|
82
|
+
`external_directory` surfaces. Those additional requests follow the same
|
|
83
|
+
bounded review and one-shot auto-confirm rules; no new surface is implicitly
|
|
84
|
+
trusted.
|
|
85
|
+
|
|
79
86
|
Automatic review stops for the current turn after three consecutive denials or
|
|
80
87
|
ten denials in the last fifty reviews. An explicit denial tells the agent that
|
|
81
88
|
automatic policy denied the request (not a human click), not to rephrase or
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erichll/pi-auto-review",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "Fail-closed, model-backed approval broker for Pi: deterministically hard-denies dangerous operations, reviews dangerous boundaries with a reviewer model, and issues one-shot expiring grants that OS sandbox adapters must consume before retrying.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@earendil-works/pi-ai": "^0.84.4",
|
|
51
51
|
"@earendil-works/pi-coding-agent": "^0.84.4",
|
|
52
|
-
"@gotgenes/pi-permission-system": "
|
|
52
|
+
"@gotgenes/pi-permission-system": ">=29.3.0 <32.0.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@earendil-works/pi-ai": "^0.84.4",
|
|
56
56
|
"@earendil-works/pi-coding-agent": "^0.84.4",
|
|
57
|
-
"@gotgenes/pi-permission-system": "
|
|
57
|
+
"@gotgenes/pi-permission-system": "31.0.0",
|
|
58
58
|
"@types/node": "^26.2.0",
|
|
59
59
|
"typescript": "^6.0.3"
|
|
60
60
|
}
|
|
@@ -75,6 +75,62 @@ function shellTokens(command: string): string[] {
|
|
|
75
75
|
return command.trim().split(/\s+/u);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
function normalizedPathToken(token: string): string {
|
|
79
|
+
let value = token.replace(/^@/, "").replace(/[;)]+$/u, "");
|
|
80
|
+
if (
|
|
81
|
+
value.length >= 2 &&
|
|
82
|
+
((value.startsWith("\"") && value.endsWith("\"")) ||
|
|
83
|
+
(value.startsWith("'") && value.endsWith("'")))
|
|
84
|
+
) {
|
|
85
|
+
value = value.slice(1, -1);
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function firstExplicitPathToken(tokens: readonly string[]): string | undefined {
|
|
91
|
+
return tokens
|
|
92
|
+
.map(normalizedPathToken)
|
|
93
|
+
.find((token) =>
|
|
94
|
+
token.startsWith("/") ||
|
|
95
|
+
token.startsWith("./") ||
|
|
96
|
+
token.startsWith("../") ||
|
|
97
|
+
token.startsWith("~/")
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Recover the path-bearing statement operands added to permission-system 31.
|
|
103
|
+
* This remains deliberately conservative rather than pretending to be a shell
|
|
104
|
+
* parser: case-arm patterns are excluded, while the actual path gate's
|
|
105
|
+
* separate decision event still records paths found in arm/body commands.
|
|
106
|
+
*/
|
|
107
|
+
function statementOperandPathToken(
|
|
108
|
+
tokens: readonly string[],
|
|
109
|
+
index: number,
|
|
110
|
+
): { handled: boolean; token?: string } {
|
|
111
|
+
const statement = tokens[index]?.replace(/^["']|["']$/gu, "");
|
|
112
|
+
if (statement !== "for" && statement !== "select" && statement !== "case") {
|
|
113
|
+
return { handled: false };
|
|
114
|
+
}
|
|
115
|
+
const inIndex = tokens.findIndex((token, tokenIndex) =>
|
|
116
|
+
tokenIndex > index && token === "in"
|
|
117
|
+
);
|
|
118
|
+
if (inIndex < 0) return { handled: true };
|
|
119
|
+
if (statement === "case") {
|
|
120
|
+
return {
|
|
121
|
+
handled: true,
|
|
122
|
+
token: firstExplicitPathToken(tokens.slice(index + 1, inIndex)),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const operands: string[] = [];
|
|
126
|
+
for (const token of tokens.slice(inIndex + 1)) {
|
|
127
|
+
if (token === "do") break;
|
|
128
|
+
operands.push(token);
|
|
129
|
+
if (/[;]$/u.test(token)) break;
|
|
130
|
+
}
|
|
131
|
+
return { handled: true, token: firstExplicitPathToken(operands) };
|
|
132
|
+
}
|
|
133
|
+
|
|
78
134
|
function executableIndex(tokens: readonly string[]): number {
|
|
79
135
|
let index = 0;
|
|
80
136
|
if (tokens[index] === "env") index++;
|
|
@@ -161,9 +217,10 @@ export function classifyBash(command: unknown, cwd: string): ClassifiedPermissio
|
|
|
161
217
|
eligible: blocker === undefined,
|
|
162
218
|
...(blocker ? { blocker } : {}),
|
|
163
219
|
} : undefined;
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
220
|
+
const statementOperand = statementOperandPathToken(tokens, index);
|
|
221
|
+
const pathToken = statementOperand.handled
|
|
222
|
+
? statementOperand.token
|
|
223
|
+
: firstExplicitPathToken(tokens.slice(index + 1));
|
|
167
224
|
return {
|
|
168
225
|
surface: "bash",
|
|
169
226
|
signature: template ?? "<command>",
|
package/src/policy.ts
CHANGED
|
@@ -202,10 +202,20 @@ function exactKeys(
|
|
|
202
202
|
);
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// Some reviewer models (notably any routed through Claude Code, which always
|
|
206
|
+
// markdown-fences JSON) wrap the decision object in a ```json ... ``` block
|
|
207
|
+
// despite the prompt forbidding markdown. Strip a single enclosing fence before
|
|
208
|
+
// parsing; the strict shape validation below is unchanged.
|
|
209
|
+
function stripReviewerFence(text: string): string {
|
|
210
|
+
const t = text.trim();
|
|
211
|
+
const fenced = t.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/i);
|
|
212
|
+
return (fenced ? fenced[1] : t).trim();
|
|
213
|
+
}
|
|
214
|
+
|
|
205
215
|
export function parseDecision(text: string): ModelDecision {
|
|
206
216
|
let value: unknown;
|
|
207
217
|
try {
|
|
208
|
-
value = JSON.parse(text
|
|
218
|
+
value = JSON.parse(stripReviewerFence(text));
|
|
209
219
|
} catch {
|
|
210
220
|
throw new Error("reviewer returned non-JSON output");
|
|
211
221
|
}
|