pi-auto-permissions 0.1.0 → 0.1.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/README.md +3 -2
- package/docs/implementation-plan.md +4 -2
- package/docs/invariants.md +3 -1
- package/package.json +1 -1
- package/src/tools/bash.ts +6 -1
- package/src/tools/denial-notice.ts +16 -0
- package/src/tools/gate.ts +6 -1
package/README.md
CHANGED
|
@@ -15,8 +15,9 @@ Permission denied. This action was not executed. No override will be requested.
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The model cannot appeal that result inside the action. It must issue a new,
|
|
18
|
-
materially safer action. The user
|
|
19
|
-
|
|
18
|
+
materially safer action. The user also receives a passive warning notice naming
|
|
19
|
+
the denied tool; it never asks for approval. The user can still deliberately
|
|
20
|
+
change the session's mode with `/perm`.
|
|
20
21
|
|
|
21
22
|
## Requirements and installation
|
|
22
23
|
|
|
@@ -20,7 +20,8 @@ The package has eight narrow components:
|
|
|
20
20
|
8. `extension.ts`: lifecycle wiring only.
|
|
21
21
|
|
|
22
22
|
No component may open a human approval dialog. UI selection belongs only to the
|
|
23
|
-
three explicit command handlers.
|
|
23
|
+
three explicit command handlers. Denial paths may emit a passive warning notice,
|
|
24
|
+
but that notice never requests approval.
|
|
24
25
|
|
|
25
26
|
## 2. Stepwise construction
|
|
26
27
|
|
|
@@ -264,7 +265,8 @@ Cover:
|
|
|
264
265
|
- explicit user commands can change settings;
|
|
265
266
|
- Off/on and reviewer changes observed by already-running sibling runtime
|
|
266
267
|
simulations;
|
|
267
|
-
- denial is an `isError` tool result visible to the model, with
|
|
268
|
+
- denial is an `isError` tool result visible to the model, with a passive user
|
|
269
|
+
warning notice and no UI dialog;
|
|
268
270
|
- the model can continue with a safer call after denial;
|
|
269
271
|
- the shipped TypeScript entrypoint loads through Pi's resource loader and the
|
|
270
272
|
no-dialog path completes in print mode.
|
package/docs/invariants.md
CHANGED
|
@@ -412,7 +412,9 @@ API. Denial returns a fixed tool result to the model:
|
|
|
412
412
|
> Permission denied. This action was not executed. No override will be requested.
|
|
413
413
|
> Choose a materially safer action.
|
|
414
414
|
|
|
415
|
-
User-facing selection UI exists only inside explicit user slash commands.
|
|
415
|
+
User-facing selection UI exists only inside explicit user slash commands. A
|
|
416
|
+
permission denial may additionally emit a passive warning notification naming
|
|
417
|
+
the denied tool; that notification is not an approval request.
|
|
416
418
|
|
|
417
419
|
### I12. Direct-tool no-self-elevation invariant
|
|
418
420
|
|
package/package.json
CHANGED
package/src/tools/bash.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type GuardianTranscriptItem,
|
|
10
10
|
} from "../guardian/index.ts";
|
|
11
11
|
import type { PermissionEngine } from "../runtime/index.ts";
|
|
12
|
+
import { notifyPermissionDenied } from "./denial-notice.ts";
|
|
12
13
|
|
|
13
14
|
export const GUARDED_BASH_METADATA = Object.freeze({
|
|
14
15
|
kind: "pi-auto-permissions-bash",
|
|
@@ -56,7 +57,10 @@ export function registerGuardedBashTool(
|
|
|
56
57
|
executionMode: "sequential",
|
|
57
58
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
58
59
|
const runtime = getRuntime();
|
|
59
|
-
if (runtime === null)
|
|
60
|
+
if (runtime === null) {
|
|
61
|
+
notifyPermissionDenied(ctx, "bash");
|
|
62
|
+
throw new Error(GUARDIAN_DENIAL_MESSAGE);
|
|
63
|
+
}
|
|
60
64
|
await runtime.refreshBackend(ctx);
|
|
61
65
|
const reviewSignal = runtime.signal(signal);
|
|
62
66
|
|
|
@@ -71,6 +75,7 @@ export function registerGuardedBashTool(
|
|
|
71
75
|
...(reviewSignal === undefined ? {} : { signal: reviewSignal }),
|
|
72
76
|
});
|
|
73
77
|
if (decision.outcome === "deny") {
|
|
78
|
+
notifyPermissionDenied(ctx, "bash");
|
|
74
79
|
if (decision.interruptTurn) ctx.abort();
|
|
75
80
|
throw new Error(decision.message);
|
|
76
81
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Keep denial feedback visible to the user without exposing tool arguments in
|
|
5
|
+
* a notification. The model still receives the fixed denial result separately.
|
|
6
|
+
*/
|
|
7
|
+
export function notifyPermissionDenied(
|
|
8
|
+
ctx: Pick<ExtensionContext, "ui">,
|
|
9
|
+
toolName: string,
|
|
10
|
+
): void {
|
|
11
|
+
try {
|
|
12
|
+
ctx.ui.notify(`Permission denied: ${toolName} action was not executed.`, "warning");
|
|
13
|
+
} catch {
|
|
14
|
+
// A notification failure must not change the fixed fail-closed denial.
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/tools/gate.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import type { GuardianTranscriptItem } from "../guardian/index.ts";
|
|
8
8
|
import { GUARDIAN_DENIAL_MESSAGE } from "../guardian/index.ts";
|
|
9
9
|
import type { PermissionEngine } from "../runtime/index.ts";
|
|
10
|
+
import { notifyPermissionDenied } from "./denial-notice.ts";
|
|
10
11
|
|
|
11
12
|
const DIRECT_FILE_TOOL_NAMES = new Set(["read", "grep", "find", "ls", "write", "edit"]);
|
|
12
13
|
|
|
@@ -26,7 +27,10 @@ export function registerPermissionToolGate(
|
|
|
26
27
|
pi.on("tool_call", async (event, ctx) => {
|
|
27
28
|
if (event.toolName === "bash") return;
|
|
28
29
|
const runtime = getRuntime();
|
|
29
|
-
if (runtime === null)
|
|
30
|
+
if (runtime === null) {
|
|
31
|
+
notifyPermissionDenied(ctx, event.toolName);
|
|
32
|
+
return { block: true, reason: GUARDIAN_DENIAL_MESSAGE };
|
|
33
|
+
}
|
|
30
34
|
|
|
31
35
|
await runtime.refreshStatus(ctx);
|
|
32
36
|
const info = findToolInfo(pi, event.toolName);
|
|
@@ -43,6 +47,7 @@ export function registerPermissionToolGate(
|
|
|
43
47
|
...(reviewSignal === undefined ? {} : { signal: reviewSignal }),
|
|
44
48
|
});
|
|
45
49
|
if (decision.outcome === "admit") return;
|
|
50
|
+
notifyPermissionDenied(ctx, event.toolName);
|
|
46
51
|
if (decision.interruptTurn) ctx.abort();
|
|
47
52
|
return { block: true, reason: decision.message };
|
|
48
53
|
});
|