@gotgenes/pi-permission-system 25.2.1 → 25.3.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 +30 -0
- package/README.md +1 -1
- package/config/config.example.json +3 -0
- package/dist/public.d.ts +156 -41
- package/docs/configuration.md +22 -2
- package/package.json +1 -1
- package/schemas/permissions.schema.json +16 -0
- package/src/access-intent/bash/command-enumeration.ts +76 -155
- package/src/access-intent/bash/nested-execution.ts +76 -0
- package/src/access-intent/bash/node-text.ts +7 -2
- package/src/access-intent/bash/token-collection.ts +47 -2
- package/src/access-intent/bash/wrapper-analysis.ts +335 -0
- package/src/authority/forwarded-request-server.ts +5 -14
- package/src/authority/local-user-authorizer.ts +2 -3
- package/src/authority/permission-prompt-component.ts +87 -47
- package/src/authority/permission-prompter.ts +9 -0
- package/src/config-loader.ts +2 -0
- package/src/config-schema.ts +14 -0
- package/src/extension-config.ts +10 -0
- package/src/handlers/gates/bash-command.ts +7 -2
- package/src/handlers/gates/bash-external-directory.ts +11 -6
- package/src/handlers/gates/bash-path.ts +10 -6
- package/src/handlers/gates/external-directory.ts +13 -8
- package/src/handlers/gates/path.ts +11 -14
- package/src/handlers/gates/skill-input.ts +5 -2
- package/src/handlers/gates/skill-read.ts +5 -6
- package/src/handlers/gates/tool.ts +10 -5
- package/src/index.ts +2 -0
- package/src/permission-prompts.ts +4 -72
- package/src/presentation/dialog-renderer.ts +404 -0
- package/src/presentation/forwarded-ask-payload.ts +45 -0
- package/src/presentation/legacy-message.ts +117 -0
- package/src/presentation/line-fitting.ts +27 -0
- package/src/presentation/path-ask-payload.ts +128 -0
- package/src/presentation/prompt-payload.ts +137 -0
- package/src/presentation/skill-ask-payload.ts +50 -0
- package/src/presentation/tool-ask-payload.ts +104 -0
- package/src/tool-preview-formatter.ts +1 -1
- package/src/types.ts +6 -0
- package/src/handlers/gates/external-directory-messages.ts +0 -28
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [25.3.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v25.2.2...pi-permission-system-v25.3.0) (2026-08-15)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **pi-permission-system:** add promptMaxRows and promptFieldMaxWidth ([8dbca30](https://github.com/gotgenes/pi-packages/commit/8dbca303f55769222f6a829d3fc7a1d7461cd334)), closes [#710](https://github.com/gotgenes/pi-packages/issues/710)
|
|
14
|
+
* **pi-permission-system:** expand the permission dialog to the complete request on Ctrl+O ([b741513](https://github.com/gotgenes/pi-packages/commit/b7415137236d6245a41f95255d17425127739a97)), closes [#710](https://github.com/gotgenes/pi-packages/issues/710)
|
|
15
|
+
* **pi-permission-system:** render permission prompts as bounded, aligned fact lines ([2bd18c5](https://github.com/gotgenes/pi-packages/commit/2bd18c57003bdfbd0f95ad2c309e5387031164a0))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
* **pi-permission-system:** distinguish the redundancy rule from elision ([5b26a46](https://github.com/gotgenes/pi-packages/commit/5b26a466191e5cc4395c2cebba22732c3d867bff)), closes [#710](https://github.com/gotgenes/pi-packages/issues/710)
|
|
21
|
+
* **pi-permission-system:** document the bounded permission dialog ([68fcf58](https://github.com/gotgenes/pi-packages/commit/68fcf583f740cbcd8c5224db96fff85a74a2b0f2)), closes [#710](https://github.com/gotgenes/pi-packages/issues/710)
|
|
22
|
+
|
|
23
|
+
## [25.2.2](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v25.2.1...pi-permission-system-v25.2.2) (2026-08-15)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* **pi-permission-system:** gate commands hosted in bash redirect targets ([1015bb8](https://github.com/gotgenes/pi-packages/commit/1015bb879234d1d96595b9f995ca88037588e16f)), closes [#741](https://github.com/gotgenes/pi-packages/issues/741)
|
|
29
|
+
* **pi-permission-system:** gate commands hosted in interpolating heredoc bodies ([48978d2](https://github.com/gotgenes/pi-packages/commit/48978d2016c67aa90f291db55d98908ba930f0d1)), closes [#741](https://github.com/gotgenes/pi-packages/issues/741)
|
|
30
|
+
* **pi-permission-system:** project path operands of heredoc-hosted nested commands ([8e2fbee](https://github.com/gotgenes/pi-packages/commit/8e2fbee404677d02c7cc565cf23d1e778994d5f0)), closes [#741](https://github.com/gotgenes/pi-packages/issues/741)
|
|
31
|
+
* **pi-permission-system:** project path operands of redirect-hosted nested commands ([12164f3](https://github.com/gotgenes/pi-packages/commit/12164f3b61b6a4b50281d84b6d741a3736516991)), closes [#741](https://github.com/gotgenes/pi-packages/issues/741)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Documentation
|
|
35
|
+
|
|
36
|
+
* **pi-permission-system:** document hosted nested-command evaluation ([f24b338](https://github.com/gotgenes/pi-packages/commit/f24b338a42855f96038fdf2efdfe440cef8501c8)), closes [#741](https://github.com/gotgenes/pi-packages/issues/741)
|
|
37
|
+
|
|
8
38
|
## [25.2.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v25.2.0...pi-permission-system-v25.2.1) (2026-08-15)
|
|
9
39
|
|
|
10
40
|
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ All permissions use one of three states:
|
|
|
66
66
|
|
|
67
67
|
When the dialog prompts, you can approve once or approve a pattern for the rest of the session.
|
|
68
68
|
In an interactive TUI session the prompt is an inline keybind dialog — `y` approve, `s` approve for this session, `n` deny, `r` deny with a reason — where each hotkey arms and a second press confirms (configurable via `doublePressToConfirm`).
|
|
69
|
-
|
|
69
|
+
The prompt shows one fact per line — who is asking, the tool, the matched rule, the value being decided — within a row budget, so a large tool input cannot take over the transcript; `Ctrl+O` (`app.tools.expand`) expands it to the complete request.
|
|
70
70
|
See [docs/configuration.md](docs/configuration.md#inline-permission-dialog-tui) for the hotkeys and [docs/session-approvals.md](docs/session-approvals.md) for session-scoped rules and pattern suggestions.
|
|
71
71
|
|
|
72
72
|
The `path` surface is a cross-cutting gate that applies to **all** file access — Pi tools, bash commands, MCP calls, and extension tools alike.
|
package/dist/public.d.ts
CHANGED
|
@@ -1,5 +1,153 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
declare const permissionStateSchema: z.ZodUnion<readonly [z.ZodLiteral<"allow">, z.ZodLiteral<"deny">, z.ZodLiteral<"ask">]>;
|
|
4
|
+
/** A permission decision. */
|
|
5
|
+
type PermissionState = z.infer<typeof permissionStateSchema>;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Provenance of a rule — which source contributed it.
|
|
9
|
+
*
|
|
10
|
+
* Config scopes: "global", "project", "agent", "project-agent".
|
|
11
|
+
* Synthesized: "builtin" (universal default / evaluate() fallback),
|
|
12
|
+
* "baseline" (conditional MCP metadata auto-allow).
|
|
13
|
+
* Runtime: "session" (session approvals).
|
|
14
|
+
* Rewrite: "yolo" (composition-stage ask→allow rewrite under yolo mode),
|
|
15
|
+
* "fail-closed" (composition-stage allow→ask floor when an
|
|
16
|
+
* invalid non-global config scope is detected).
|
|
17
|
+
*/
|
|
18
|
+
type RuleOrigin = "global" | "project" | "agent" | "project-agent" | "builtin" | "baseline" | "session" | "yolo" | "fail-closed";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Execution context of a bash command nested inside a substitution or subshell.
|
|
22
|
+
* Absent for current-shell (top-level) commands.
|
|
23
|
+
*/
|
|
24
|
+
type BashCommandContext = "command_substitution" | "process_substitution" | "subshell";
|
|
25
|
+
interface PermissionCheckResult {
|
|
26
|
+
toolName: string;
|
|
27
|
+
state: PermissionState;
|
|
28
|
+
/** Custom denial reason from a deny-with-reason pattern, when present. */
|
|
29
|
+
reason?: string;
|
|
30
|
+
matchedPattern?: string;
|
|
31
|
+
command?: string;
|
|
32
|
+
target?: string;
|
|
33
|
+
source: "tool" | "bash" | "mcp" | "skill" | "special" | "default" | "session";
|
|
34
|
+
/** Which source contributed the winning rule. */
|
|
35
|
+
origin: RuleOrigin;
|
|
36
|
+
/**
|
|
37
|
+
* Execution context of the offending nested command, when the winning bash
|
|
38
|
+
* unit came from a substitution or subshell. Absent for current-shell
|
|
39
|
+
* (top-level) commands.
|
|
40
|
+
*/
|
|
41
|
+
commandContext?: BashCommandContext;
|
|
42
|
+
/**
|
|
43
|
+
* The command the winning bash unit actually runs, when it is a wrapper whose
|
|
44
|
+
* inner command differs from the unit text (#713). Display-only: the gate
|
|
45
|
+
* still decides on `command`, so this never widens or narrows a decision.
|
|
46
|
+
*/
|
|
47
|
+
executedUnit?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The complete, structured description of a permission ask (ADR 0011 §2).
|
|
52
|
+
*
|
|
53
|
+
* A gate emits one of these instead of a sentence. It is complete by contract:
|
|
54
|
+
* it never truncates and never decides what a human will see. Every consumer is
|
|
55
|
+
* a renderer over it, eliding under its own budget — so elision is a property
|
|
56
|
+
* of a render, never of the payload.
|
|
57
|
+
*/
|
|
58
|
+
interface PromptPayload {
|
|
59
|
+
readonly kind: PromptPayloadKind;
|
|
60
|
+
readonly request: PromptRequestFacts;
|
|
61
|
+
/** Complete; each renderer elides to fit its own budget. */
|
|
62
|
+
readonly evidence: readonly PromptEvidence[];
|
|
63
|
+
/** Supplied by registered annotators; always marked as model-generated. */
|
|
64
|
+
readonly annotations: readonly PromptAnnotation[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Which ask this payload describes — the renderers' dispatch discriminant.
|
|
68
|
+
*
|
|
69
|
+
* Present because the ask shapes are not separable by surface alone: a tool
|
|
70
|
+
* external-directory ask and a bash one share the `external_directory` surface,
|
|
71
|
+
* and the `path` gate and the per-tool gate differ only in wording. It mirrors
|
|
72
|
+
* `DenialContext`'s discriminated union, the shape ADR 0011 §7 names as already
|
|
73
|
+
* correct, and gives every renderer an exhaustive switch rather than a set of
|
|
74
|
+
* string comparisons a new variant sails past.
|
|
75
|
+
*/
|
|
76
|
+
type PromptPayloadKind = "bash" | "mcp" | "tool" | "path" | "external_directory" | "bash_external_directory" | "skill" | "skill_read" | "forwarded";
|
|
77
|
+
/**
|
|
78
|
+
* The invariant core (ADR 0011 §3): the facts visible in every render, that no
|
|
79
|
+
* renderer's budget may elide.
|
|
80
|
+
*
|
|
81
|
+
* Named for what it holds — the permission request's own facts, matching the
|
|
82
|
+
* package's `PermissionRequest` / `ForwardedPermissionRequest` vocabulary —
|
|
83
|
+
* rather than for its contract, which this comment states instead.
|
|
84
|
+
*/
|
|
85
|
+
interface PromptRequestFacts {
|
|
86
|
+
/** Who is asking, and whether the ask arrived from a subagent. */
|
|
87
|
+
readonly requester: PromptRequester;
|
|
88
|
+
/** The gate surface the rule fired on. */
|
|
89
|
+
readonly surface: string;
|
|
90
|
+
/** The gated tool name; `null` when the ask is not tool-shaped. */
|
|
91
|
+
readonly toolName: string | null;
|
|
92
|
+
/**
|
|
93
|
+
* The invoked tool name when a shell alias re-exposes bash under another
|
|
94
|
+
* name (#574) — "gated as bash, invoked as exec_command" is two facts.
|
|
95
|
+
* `null` when it adds nothing.
|
|
96
|
+
*/
|
|
97
|
+
readonly invokedToolName: string | null;
|
|
98
|
+
/** The decision-relevant value: the command, path, MCP target, or skill name. */
|
|
99
|
+
readonly value: string;
|
|
100
|
+
/** The matched rule, including a sentinel such as `<indirection-bash-wrapper>`. */
|
|
101
|
+
readonly matchedPattern: string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Where the offending bash unit runs, when it came from a substitution or a
|
|
104
|
+
* subshell. A fact rather than a rendered clause: it is what makes the
|
|
105
|
+
* matched rule intelligible, and how it reads is the renderer's choice.
|
|
106
|
+
*/
|
|
107
|
+
readonly commandContext: BashCommandContext | null;
|
|
108
|
+
/**
|
|
109
|
+
* For bash, the unit that will actually run — including inside an unstrippable
|
|
110
|
+
* wrapper (#713). `null` when it adds nothing over {@link value}.
|
|
111
|
+
*/
|
|
112
|
+
readonly executedUnit: string | null;
|
|
113
|
+
}
|
|
114
|
+
/** Who is asking, one hop below when the ask was forwarded. */
|
|
115
|
+
interface PromptRequester {
|
|
116
|
+
readonly agentName: string | null;
|
|
117
|
+
readonly forwarded: boolean;
|
|
118
|
+
/** The requesting session, for a forwarded ask; `null` for a local one. */
|
|
119
|
+
readonly sessionId: string | null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* One piece of decision evidence.
|
|
123
|
+
*
|
|
124
|
+
* Complete on the payload; each renderer elides entries and orders them under
|
|
125
|
+
* its own budget (ADR 0011 §4).
|
|
126
|
+
*/
|
|
127
|
+
interface PromptEvidence {
|
|
128
|
+
readonly label: string;
|
|
129
|
+
readonly text: string;
|
|
130
|
+
/**
|
|
131
|
+
* A secondary fact bound to this entry that a renderer may show alongside
|
|
132
|
+
* {@link text} or elide independently — a path's symlink-resolved alias, for
|
|
133
|
+
* instance. Bound to the entry rather than listed as a second one so an
|
|
134
|
+
* elision cannot separate the two.
|
|
135
|
+
*/
|
|
136
|
+
readonly detail: string | null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* A model-generated advisory (ADR 0011 §8).
|
|
140
|
+
*
|
|
141
|
+
* The slot owns the attribution and the model-generated marking, so marking is
|
|
142
|
+
* a property of the payload rather than a discipline each annotator must
|
|
143
|
+
* remember. Structurally separate from any verdict: an annotation cannot allow,
|
|
144
|
+
* deny, defer, or suppress.
|
|
145
|
+
*/
|
|
146
|
+
interface PromptAnnotation {
|
|
147
|
+
readonly source: string;
|
|
148
|
+
readonly text: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
3
151
|
/** Emitted at `session_start`, after the service is published. */
|
|
4
152
|
declare const PERMISSIONS_READY_CHANNEL = "permissions:ready";
|
|
5
153
|
/** Emitted when a permission request is committed to the active UI prompt path. */
|
|
@@ -75,47 +223,6 @@ interface PermissionDecisionEvent {
|
|
|
75
223
|
matchedPattern: string | null;
|
|
76
224
|
}
|
|
77
225
|
|
|
78
|
-
declare const permissionStateSchema: z.ZodUnion<readonly [z.ZodLiteral<"allow">, z.ZodLiteral<"deny">, z.ZodLiteral<"ask">]>;
|
|
79
|
-
/** A permission decision. */
|
|
80
|
-
type PermissionState = z.infer<typeof permissionStateSchema>;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Provenance of a rule — which source contributed it.
|
|
84
|
-
*
|
|
85
|
-
* Config scopes: "global", "project", "agent", "project-agent".
|
|
86
|
-
* Synthesized: "builtin" (universal default / evaluate() fallback),
|
|
87
|
-
* "baseline" (conditional MCP metadata auto-allow).
|
|
88
|
-
* Runtime: "session" (session approvals).
|
|
89
|
-
* Rewrite: "yolo" (composition-stage ask→allow rewrite under yolo mode),
|
|
90
|
-
* "fail-closed" (composition-stage allow→ask floor when an
|
|
91
|
-
* invalid non-global config scope is detected).
|
|
92
|
-
*/
|
|
93
|
-
type RuleOrigin = "global" | "project" | "agent" | "project-agent" | "builtin" | "baseline" | "session" | "yolo" | "fail-closed";
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Execution context of a bash command nested inside a substitution or subshell.
|
|
97
|
-
* Absent for current-shell (top-level) commands.
|
|
98
|
-
*/
|
|
99
|
-
type BashCommandContext = "command_substitution" | "process_substitution" | "subshell";
|
|
100
|
-
interface PermissionCheckResult {
|
|
101
|
-
toolName: string;
|
|
102
|
-
state: PermissionState;
|
|
103
|
-
/** Custom denial reason from a deny-with-reason pattern, when present. */
|
|
104
|
-
reason?: string;
|
|
105
|
-
matchedPattern?: string;
|
|
106
|
-
command?: string;
|
|
107
|
-
target?: string;
|
|
108
|
-
source: "tool" | "bash" | "mcp" | "skill" | "special" | "default" | "session";
|
|
109
|
-
/** Which source contributed the winning rule. */
|
|
110
|
-
origin: RuleOrigin;
|
|
111
|
-
/**
|
|
112
|
-
* Execution context of the offending nested command, when the winning bash
|
|
113
|
-
* unit came from a substitution or subshell. Absent for current-shell
|
|
114
|
-
* (top-level) commands.
|
|
115
|
-
*/
|
|
116
|
-
commandContext?: BashCommandContext;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
226
|
/**
|
|
120
227
|
* The child's session-approval suggestion, relayed to the serving node so a
|
|
121
228
|
* human who grants "the whole session" records the same pattern the child
|
|
@@ -169,6 +276,14 @@ interface PromptPermissionDetails {
|
|
|
169
276
|
source: PermissionReviewSource;
|
|
170
277
|
agentName: string | null;
|
|
171
278
|
message: string;
|
|
279
|
+
/**
|
|
280
|
+
* The complete structured description of this ask (ADR 0011 §2).
|
|
281
|
+
*
|
|
282
|
+
* Required: every ask carries one, and the type is what guarantees it rather
|
|
283
|
+
* than a convention each gate has to remember. `message` is a render over it
|
|
284
|
+
* for the duration of the transition, so the two cannot disagree.
|
|
285
|
+
*/
|
|
286
|
+
payload: PromptPayload;
|
|
172
287
|
toolCallId?: string;
|
|
173
288
|
toolName?: string;
|
|
174
289
|
skillName?: string;
|
package/docs/configuration.md
CHANGED
|
@@ -36,7 +36,7 @@ See [migration/0644-project-trust-gating.md](migration/0644-project-trust-gating
|
|
|
36
36
|
4. Project agent frontmatter
|
|
37
37
|
|
|
38
38
|
The `permission` object uses deep-shallow merge: string-vs-string replaces; both-object shallow-merges pattern maps; string-vs-object the override wins entirely.
|
|
39
|
-
Scalar fields (`debugLog`, `permissionReviewLog`, `yoloMode`, `doublePressToConfirm`, `forwardingTimeoutMs`) use simple replacement.
|
|
39
|
+
Scalar fields (`debugLog`, `permissionReviewLog`, `yoloMode`, `doublePressToConfirm`, `forwardingTimeoutMs`, `promptMaxRows`, `promptFieldMaxWidth`) use simple replacement.
|
|
40
40
|
|
|
41
41
|
**Invalid higher-precedence scope fails closed.**
|
|
42
42
|
If a non-global scope (project config, global agent frontmatter, or project agent frontmatter) is present but fails to load or validate, it no longer contributes an empty scope that silently inherits the lower scope's rules.
|
|
@@ -106,6 +106,8 @@ This clamp is deny-preserving and, like `yoloMode`, applied at composition; when
|
|
|
106
106
|
| `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
|
|
107
107
|
| `doublePressToConfirm` | `true` | Requires a confirming second press of a decision hotkey in the inline TUI dialog (see below). TUI sessions only; set to `false` for single-press. |
|
|
108
108
|
| `forwardingTimeoutMs` | `600000` | How long a subagent waits for the parent session to answer a forwarded permission request, in milliseconds. A child whose in-process parent is not draining its inbox gives up in ~2 s regardless. |
|
|
109
|
+
| `promptMaxRows` | `24` | Max rows a permission prompt renders before eliding its evidence. The request's own facts are never elided by this budget; `Ctrl+O` expands the prompt to the complete request. |
|
|
110
|
+
| `promptFieldMaxWidth` | `400` | Max characters of any one field shown in a permission prompt. This is what bounds a single long field (a here-string command, say) that would otherwise fill the prompt through wrapping. |
|
|
109
111
|
| `toolInputPreviewMaxLength` | `200` | Max characters of inline JSON shown in permission prompts for tool inputs. Omit to use the default. Set to a large value to disable truncation. |
|
|
110
112
|
| `toolTextSummaryMaxLength` | `80` | Max characters of inline pattern/path summaries (grep patterns, find globs, ls paths) in permission prompts. Omit to use the default. |
|
|
111
113
|
| `piInfrastructureReadPaths` | `[]` | Extra directories to auto-allow for reads, bypassing the `external_directory` gate. Supports `~`/`$HOME`/`${HOME}` expansion and wildcard patterns (`*`, `?`). |
|
|
@@ -129,10 +131,23 @@ Arrow keys / `j`/`k` move the highlight, `enter` confirms the highlighted option
|
|
|
129
131
|
With `doublePressToConfirm` enabled (the default), a letter hotkey **arms** its action and shows a `Press y again to approve.` hint; press the same key again to commit.
|
|
130
132
|
Set `doublePressToConfirm` to `false` to commit on the first press.
|
|
131
133
|
|
|
132
|
-
Pi's tool-expansion binding (`app.tools.expand`, `Ctrl+O` by default) stays live while the dialog is open
|
|
134
|
+
Pi's tool-expansion binding (`app.tools.expand`, `Ctrl+O` by default) stays live while the dialog is open.
|
|
135
|
+
It expands both the prompt itself — to the complete request, unbounded by `promptMaxRows` and `promptFieldMaxWidth` — and the host's pending tool call, so one keystroke shows you everything before you decide.
|
|
133
136
|
It only toggles the display — it never resolves, commits, or arms the pending decision.
|
|
134
137
|
While you are typing a denial reason it is not intercepted, so a rebound printable key still reaches the reason editor.
|
|
135
138
|
|
|
139
|
+
### What a prompt shows
|
|
140
|
+
|
|
141
|
+
The prompt renders one fact per line, with the requesting agent (and, for a forwarded subagent ask, its session), the tool, the gate surface, the matched rule, the decision-relevant value, and — for a wrapper such as `xargs` — the command that will actually run.
|
|
142
|
+
Those facts are always present: a budget may shorten a long one, never drop it.
|
|
143
|
+
A fact the line above already states is not repeated — a bash ask shows `tool : bash` without a second `surface : bash` line, and a path ask's `path :` line names the surface itself.
|
|
144
|
+
|
|
145
|
+
Everything else is evidence — the full command a gated sub-command came from, the working directory a path escaped, the tool-input preview — and it is what gives way when the render does not fit.
|
|
146
|
+
A shortened field or a dropped entry is marked with an ellipsis, and `Ctrl+O` shows the complete request.
|
|
147
|
+
Raise `promptMaxRows` to see more evidence inline; raise `promptFieldMaxWidth` to see more of a long command.
|
|
148
|
+
|
|
149
|
+
Non-TUI contexts render the same facts under the same budget, without the colour or the expansion.
|
|
150
|
+
|
|
136
151
|
Non-TUI contexts (RPC / frontend-driven sessions) keep the single-select prompt and are unaffected by `doublePressToConfirm`.
|
|
137
152
|
|
|
138
153
|
### `piInfrastructureReadPaths` patterns
|
|
@@ -328,6 +343,11 @@ Quotes are respected (an operator inside `'…'` or `"…"` does not split the c
|
|
|
328
343
|
Commands nested inside command substitution (`$(…)`, backticks), process substitution (`<(…)`/`>(…)`), and subshells (`( … )`) are evaluated against the bash patterns too, in addition to their enclosing command — since those inner commands really execute.
|
|
329
344
|
So `echo $(rm -rf foo)` evaluates both `echo $(rm -rf foo)` and the inner `rm -rf foo`; if `rm *` is denied, the whole invocation is denied.
|
|
330
345
|
The deny reason and the approval prompt note the nested origin (e.g. `inside command substitution`).
|
|
346
|
+
|
|
347
|
+
This holds wherever the substitution appears, not only in argument position.
|
|
348
|
+
A substitution in a **redirect target** (`echo hi > $(rm *.txt)`, `cat < <(rm c)`, ``echo hi 2> `rm d` ``) and one in an **interpolating heredoc body** (`cat <<EOF` with `$(rm e)` in the body) are evaluated the same way.
|
|
349
|
+
A quoted heredoc delimiter (`<<'EOF'` or `<<"EOF"`) does not interpolate, so its body is literal text and nothing in it is evaluated as a command.
|
|
350
|
+
The enclosing command is still matched without its redirect, so a rule like `npm install` keeps matching `npm install > out.txt`.
|
|
331
351
|
Control-flow bodies (`if`/`while`/`for`/`case`) and `{ … }` brace groups are not descended into; their contents are matched as part of the enclosing statement's text.
|
|
332
352
|
|
|
333
353
|
A leading environment-variable assignment prefix is stripped before matching, so the rule gates the underlying command rather than the prefix.
|
package/package.json
CHANGED
|
@@ -39,6 +39,22 @@
|
|
|
39
39
|
"minimum": 1,
|
|
40
40
|
"maximum": 9007199254740991
|
|
41
41
|
},
|
|
42
|
+
"promptMaxRows": {
|
|
43
|
+
"description": "Maximum rows a permission prompt renders before eliding its evidence. Omit to use the default (24).",
|
|
44
|
+
"markdownDescription": "Maximum rows a permission prompt renders before eliding its evidence.\n\nOmit to use the default (24). The request's own facts — the requesting agent, the tool, the matched rule, the decision-relevant value — are never elided by this budget; what gives way is the supporting evidence, and `Ctrl+O` expands the prompt to the complete request.",
|
|
45
|
+
"default": 24,
|
|
46
|
+
"type": "integer",
|
|
47
|
+
"minimum": 1,
|
|
48
|
+
"maximum": 9007199254740991
|
|
49
|
+
},
|
|
50
|
+
"promptFieldMaxWidth": {
|
|
51
|
+
"description": "Maximum characters of any one field shown in a permission prompt. Omit to use the default (400).",
|
|
52
|
+
"markdownDescription": "Maximum characters of any one field shown in a permission prompt.\n\nOmit to use the default (400). This is what bounds a single pathological field — a long here-string command, say — that would otherwise fill the prompt through wrapping. A shortened field is marked with an ellipsis, and `Ctrl+O` shows it in full.",
|
|
53
|
+
"default": 400,
|
|
54
|
+
"type": "integer",
|
|
55
|
+
"minimum": 1,
|
|
56
|
+
"maximum": 9007199254740991
|
|
57
|
+
},
|
|
42
58
|
"toolInputPreviewMaxLength": {
|
|
43
59
|
"description": "Maximum character length of the inline-JSON tool-input preview shown in permission prompts. Omit to use the default (200). Set to a large value to disable truncation.",
|
|
44
60
|
"markdownDescription": "Maximum character length of the inline-JSON tool-input preview shown in permission prompts.\n\nOmit to use the default (200). Set to a large value (e.g. `10000`) to effectively disable truncation and see the full input.",
|