@erichll/pi-auto-review 0.17.0 → 0.18.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 +11 -0
- package/README.md +6 -5
- package/package.json +2 -2
- package/src/user-feedback.ts +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0 - 2026-09-11
|
|
4
|
+
|
|
5
|
+
- Coordinated release for `@erichll/pi-sandbox 0.18.0`.
|
|
6
|
+
- Dismiss the above-editor widget eight seconds after an allow or auto-confirm
|
|
7
|
+
so a successful check does not stay on screen until the next review. Denials,
|
|
8
|
+
deferrals, and local-confirmation waits still remain until the next check.
|
|
9
|
+
- Raise the `@gotgenes/pi-permission-system` peer floor to `>=30.0.0` and drop
|
|
10
|
+
the upper bound. 29.x is no longer claimed; 32.x does not change the public
|
|
11
|
+
authorizer API this package uses, and later majors are no longer excluded by
|
|
12
|
+
the range.
|
|
13
|
+
|
|
3
14
|
## 0.17.0 - 2026-09-05
|
|
4
15
|
|
|
5
16
|
- Coordinated release for `@erichll/pi-sandbox 0.17.0`; the broker API and
|
package/README.md
CHANGED
|
@@ -31,8 +31,7 @@ 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. This release line supports permission-system
|
|
35
|
-
> latest 31.x release:
|
|
34
|
+
> extension. This release line supports permission-system 30.0.0 and later:
|
|
36
35
|
|
|
37
36
|
Node.js 22.13.0 or newer is required. Permission auditing uses Node's built-in
|
|
38
37
|
`node:sqlite`; it does not require a SQLite CLI, system SQLite library, or npm
|
|
@@ -178,9 +177,11 @@ Interactive sessions show the current permission check in a single widget
|
|
|
178
177
|
above the editor. Each check first shows its surface, compact target, and the
|
|
179
178
|
dynamically configured reviewer model, then replaces that content in place
|
|
180
179
|
with the outcome, target and rationale, model, token usage, duration, and any
|
|
181
|
-
extra call count. A new check replaces the previous result
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
extra call count. A new check replaces the previous result. Allowed and
|
|
181
|
+
auto-confirmed results dismiss after eight seconds so they do not occupy the
|
|
182
|
+
editor; denials, deferrals, and local-confirmation waits stay until the next
|
|
183
|
+
check, a session change, or shutdown. Concurrent older checks cannot overwrite
|
|
184
|
+
the most recently started one.
|
|
184
185
|
|
|
185
186
|
Every request still has its own model call, verdict, grant, local confirmation,
|
|
186
187
|
and audit record. No new review-result transcript entries are written. Existing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erichll/pi-auto-review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
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,7 +49,7 @@
|
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@earendil-works/pi-ai": "^0.85.0",
|
|
51
51
|
"@earendil-works/pi-coding-agent": "^0.85.0",
|
|
52
|
-
"@gotgenes/pi-permission-system": ">=
|
|
52
|
+
"@gotgenes/pi-permission-system": ">=30.0.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@earendil-works/pi-ai": "^0.85.0",
|
package/src/user-feedback.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
export const USER_REVIEW_STATUS_KEY = "pi-auto-review";
|
|
4
4
|
export const USER_REVIEW_ENTRY_TYPE = "pi-auto-review";
|
|
5
5
|
export const USER_REVIEW_WIDGET_KEY = "pi-auto-review";
|
|
6
|
+
/** How long a successful completed widget stays above the editor. */
|
|
7
|
+
export const USER_REVIEW_SUCCESS_WIDGET_TTL_MS = 8_000;
|
|
6
8
|
|
|
7
9
|
export type UserReviewOutcome =
|
|
8
10
|
| "allow"
|
|
@@ -652,9 +654,14 @@ function setWidget(
|
|
|
652
654
|
}
|
|
653
655
|
}
|
|
654
656
|
|
|
657
|
+
function isTransientCompletedOutcome(outcome: UserReviewOutcome): boolean {
|
|
658
|
+
return outcome === "allow" || outcome === "auto_confirm";
|
|
659
|
+
}
|
|
660
|
+
|
|
655
661
|
/** Owns the single live widget and rejects stale concurrent completions. */
|
|
656
662
|
export class UserReviewWidgetController {
|
|
657
663
|
#generation = 0;
|
|
664
|
+
#dismissTimer: ReturnType<typeof setTimeout> | undefined;
|
|
658
665
|
/** Set while a `ctx.ui` prompt blocks the session (ui_prompt span). */
|
|
659
666
|
#waiting?: { kind?: unknown; title?: unknown };
|
|
660
667
|
#current?: {
|
|
@@ -664,6 +671,9 @@ export class UserReviewWidgetController {
|
|
|
664
671
|
data?: UserReviewWidgetData;
|
|
665
672
|
notice?: UserReviewNotice;
|
|
666
673
|
};
|
|
674
|
+
constructor(
|
|
675
|
+
private readonly successTtlMs = USER_REVIEW_SUCCESS_WIDGET_TTL_MS,
|
|
676
|
+
) {}
|
|
667
677
|
|
|
668
678
|
begin(
|
|
669
679
|
requestId: string,
|
|
@@ -671,6 +681,7 @@ export class UserReviewWidgetController {
|
|
|
671
681
|
input: { surface: string; target?: string; model?: string },
|
|
672
682
|
): number {
|
|
673
683
|
const generation = ++this.#generation;
|
|
684
|
+
this.#cancelDismiss();
|
|
674
685
|
this.#current = { requestId, generation, ctx, data: buildUserReviewingWidgetData(input) };
|
|
675
686
|
this.#render();
|
|
676
687
|
return generation;
|
|
@@ -715,8 +726,12 @@ export class UserReviewWidgetController {
|
|
|
715
726
|
return;
|
|
716
727
|
}
|
|
717
728
|
if (!this.#isCurrent(requestId, generation)) return;
|
|
729
|
+
this.#cancelDismiss();
|
|
718
730
|
this.#current = { requestId, generation, ctx, notice, data };
|
|
719
731
|
if (!setWidget(ctx, data)) notifyUserReview(ctx, notice);
|
|
732
|
+
if (isTransientCompletedOutcome(data.outcome)) {
|
|
733
|
+
this.#scheduleDismiss(generation, ctx);
|
|
734
|
+
}
|
|
720
735
|
}
|
|
721
736
|
|
|
722
737
|
permissionDecision(event: unknown): void {
|
|
@@ -738,18 +753,36 @@ export class UserReviewWidgetController {
|
|
|
738
753
|
type: current.notice?.type ?? data.type,
|
|
739
754
|
message: data.lines.join("\n"),
|
|
740
755
|
};
|
|
756
|
+
this.#cancelDismiss();
|
|
741
757
|
this.#current = { ...current, data, notice };
|
|
742
758
|
if (!setWidget(current.ctx, data)) notifyUserReview(current.ctx, notice);
|
|
743
759
|
}
|
|
744
760
|
|
|
745
761
|
clear(ctx?: ExtensionContext): void {
|
|
746
762
|
++this.#generation;
|
|
763
|
+
this.#cancelDismiss();
|
|
747
764
|
this.#waiting = undefined;
|
|
748
765
|
const current = this.#current;
|
|
749
766
|
this.#current = undefined;
|
|
750
767
|
if (current) setWidget(ctx ?? current.ctx, undefined);
|
|
751
768
|
}
|
|
752
769
|
|
|
770
|
+
#cancelDismiss(): void {
|
|
771
|
+
if (this.#dismissTimer === undefined) return;
|
|
772
|
+
clearTimeout(this.#dismissTimer);
|
|
773
|
+
this.#dismissTimer = undefined;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
#scheduleDismiss(generation: number, ctx: ExtensionContext): void {
|
|
777
|
+
if (this.successTtlMs <= 0) return;
|
|
778
|
+
this.#dismissTimer = setTimeout(() => {
|
|
779
|
+
this.#dismissTimer = undefined;
|
|
780
|
+
if (this.#current?.generation !== generation) return;
|
|
781
|
+
this.clear(ctx);
|
|
782
|
+
}, this.successTtlMs);
|
|
783
|
+
this.#dismissTimer.unref?.();
|
|
784
|
+
}
|
|
785
|
+
|
|
753
786
|
#isCurrent(requestId: string, generation: number): boolean {
|
|
754
787
|
return this.#current?.requestId === requestId &&
|
|
755
788
|
this.#current.generation === generation;
|