@erichll/pi-auto-review 0.8.0 → 0.8.1

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1 - 2026-08-21
4
+
5
+ - Restore TUI auto-confirm for capped `path` / `external_directory` allows on
6
+ permission-system 26.x. `permissions:ui_prompt` no longer carries `message`;
7
+ the bridge now binds `requestId` plus `request.value` (falling back to the
8
+ display `value`, then the legacy `message`) and fingerprints the live dialog
9
+ with those fields. Top-level display `surface` is not treated as the gate.
10
+
3
11
  ## 0.8.0 - 2026-08-21
4
12
 
5
13
  - Add `/auto-review-approve` for exact non-critical denial retries and remove
package/README.md CHANGED
@@ -4,7 +4,8 @@ A fail-closed, model-backed boundary reviewer for the Pi coding agent.
4
4
 
5
5
  The extension participates in `@gotgenes/pi-permission-system` as the
6
6
  `pi-auto-review` authorizer and exposes a process-local broker for OS sandbox
7
- adapters. The npm package and reviewer model have separate names:
7
+ adapters. It is an authorizer *inside* the permission system, so installing
8
+ that dependency is a hard prerequisite (see [Install and enable](#install-and-enable)). The npm package and reviewer model have separate names:
8
9
 
9
10
  - package: `@erichll/pi-auto-review`
10
11
  - authorizer: `pi-auto-review`
@@ -12,12 +13,18 @@ adapters. The npm package and reviewer model have separate names:
12
13
 
13
14
  ## Install and enable
14
15
 
15
- Install the package outside the agent-writable workspace:
16
+ > **Prerequisite:** pi-auto-review is an authorizer inside
17
+ > `@gotgenes/pi-permission-system`. Pi does not auto-install peer packages, so
18
+ > install the permission system separately (once per machine) before this
19
+ > extension:
16
20
 
17
21
  ```bash
22
+ pi install npm:@gotgenes/pi-permission-system
18
23
  pi install npm:@erichll/pi-auto-review
19
24
  ```
20
25
 
26
+ Install the package outside the agent-writable workspace:
27
+
21
28
  Add it to the permission-system authorizer chain:
22
29
 
23
30
  ```json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erichll/pi-auto-review",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Model-backed boundary approval broker for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,12 +49,12 @@
49
49
  "peerDependencies": {
50
50
  "@earendil-works/pi-ai": "^0.84.1",
51
51
  "@earendil-works/pi-coding-agent": "^0.84.1",
52
- "@gotgenes/pi-permission-system": "^25.1.0"
52
+ "@gotgenes/pi-permission-system": "^26.0.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@earendil-works/pi-ai": "^0.84.1",
56
56
  "@earendil-works/pi-coding-agent": "^0.84.1",
57
- "@gotgenes/pi-permission-system": "^25.1.0",
57
+ "@gotgenes/pi-permission-system": "^26.0.0",
58
58
  "@types/node": "^26.2.0",
59
59
  "typescript": "^6.0.3"
60
60
  }
@@ -7,9 +7,15 @@ type UiCustom = ExtensionUIContext["custom"];
7
7
 
8
8
  type PermissionUiPromptEvent = {
9
9
  requestId: string;
10
- message: string;
10
+ /** Value the live dialog is expected to show (command, path, or legacy message). */
11
+ fingerprint: string;
12
+ /** Gate surface from `request.surface`, when the 26.x payload carries it. */
13
+ gateSurface?: string;
11
14
  };
12
15
 
16
+ /** Prefix length that survives the default 400-char prompt field cap. */
17
+ const FINGERPRINT_PREFIX = 80;
18
+
13
19
  type PendingApproval = {
14
20
  surface: string;
15
21
  expiresAt: number;
@@ -79,6 +85,9 @@ export class PermissionUiAutoConfirmer {
79
85
  this.pending.delete(event.requestId);
80
86
 
81
87
  if (!this.enabledSurfaces().includes(pending.surface)) return;
88
+ // Top-level event.surface is the display tool name (e.g. bash), not the
89
+ // gate. Only request.surface is comparable to the staged allow.
90
+ if (event.gateSurface && event.gateSurface !== pending.surface) return;
82
91
  this.settleActiveForConflict();
83
92
 
84
93
  const attempt: PromptAttempt = {
@@ -258,13 +267,24 @@ function parsePermissionUiPromptEvent(
258
267
  raw: unknown,
259
268
  ): PermissionUiPromptEvent | undefined {
260
269
  if (!isRecord(raw)) return;
261
- if (
262
- typeof raw.requestId !== "string" ||
263
- typeof raw.message !== "string"
264
- ) {
265
- return;
266
- }
267
- return { requestId: raw.requestId, message: raw.message };
270
+ const requestId = nonemptyString(raw.requestId);
271
+ if (!requestId) return;
272
+
273
+ const request = isRecord(raw.request) ? raw.request : undefined;
274
+ // permission-system 26+ removed `message`. Prefer the structured value the
275
+ // dialog actually renders, then the display projection, then the legacy
276
+ // assembled sentence.
277
+ const fingerprint =
278
+ nonemptyString(request?.value) ??
279
+ nonemptyString(raw.value) ??
280
+ nonemptyString(raw.message);
281
+ if (!fingerprint) return;
282
+
283
+ return {
284
+ requestId,
285
+ fingerprint,
286
+ gateSurface: nonemptyString(request?.surface),
287
+ };
268
288
  }
269
289
 
270
290
  function isPermissionPromptComponent(
@@ -280,17 +300,23 @@ function isPermissionPromptComponent(
280
300
  const rendered = value.render(2_000);
281
301
  if (!Array.isArray(rendered)) return false;
282
302
  const plain = normalizeWhitespace(stripAnsi(rendered.join("\n")));
283
- const messagePrefix = normalizeWhitespace(event.message).slice(0, 120);
303
+ const needle = event.fingerprint.slice(0, FINGERPRINT_PREFIX);
284
304
  return (
285
305
  plain.includes("Permission Required") &&
286
- messagePrefix.length > 0 &&
287
- plain.includes(messagePrefix)
306
+ needle.length > 0 &&
307
+ plain.includes(needle)
288
308
  );
289
309
  } catch {
290
310
  return false;
291
311
  }
292
312
  }
293
313
 
314
+ function nonemptyString(value: unknown): string | undefined {
315
+ if (typeof value !== "string") return;
316
+ const compact = normalizeWhitespace(value);
317
+ return compact || undefined;
318
+ }
319
+
294
320
  function invokeUiCustom(
295
321
  method: UiCustom,
296
322
  ui: ExtensionUIContext,