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 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 can still deliberately change the session's
19
- mode with `/perm`.
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 no UI dialog;
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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-auto-permissions",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A minimal Codex-style Auto/Unrestricted permission guardrail for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
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) throw new Error(GUARDIAN_DENIAL_MESSAGE);
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) return { block: true, reason: GUARDIAN_DENIAL_MESSAGE };
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
  });