@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +1 -1
  3. package/config/config.example.json +3 -0
  4. package/dist/public.d.ts +156 -41
  5. package/docs/configuration.md +22 -2
  6. package/package.json +1 -1
  7. package/schemas/permissions.schema.json +16 -0
  8. package/src/access-intent/bash/command-enumeration.ts +76 -155
  9. package/src/access-intent/bash/nested-execution.ts +76 -0
  10. package/src/access-intent/bash/node-text.ts +7 -2
  11. package/src/access-intent/bash/token-collection.ts +47 -2
  12. package/src/access-intent/bash/wrapper-analysis.ts +335 -0
  13. package/src/authority/forwarded-request-server.ts +5 -14
  14. package/src/authority/local-user-authorizer.ts +2 -3
  15. package/src/authority/permission-prompt-component.ts +87 -47
  16. package/src/authority/permission-prompter.ts +9 -0
  17. package/src/config-loader.ts +2 -0
  18. package/src/config-schema.ts +14 -0
  19. package/src/extension-config.ts +10 -0
  20. package/src/handlers/gates/bash-command.ts +7 -2
  21. package/src/handlers/gates/bash-external-directory.ts +11 -6
  22. package/src/handlers/gates/bash-path.ts +10 -6
  23. package/src/handlers/gates/external-directory.ts +13 -8
  24. package/src/handlers/gates/path.ts +11 -14
  25. package/src/handlers/gates/skill-input.ts +5 -2
  26. package/src/handlers/gates/skill-read.ts +5 -6
  27. package/src/handlers/gates/tool.ts +10 -5
  28. package/src/index.ts +2 -0
  29. package/src/permission-prompts.ts +4 -72
  30. package/src/presentation/dialog-renderer.ts +404 -0
  31. package/src/presentation/forwarded-ask-payload.ts +45 -0
  32. package/src/presentation/legacy-message.ts +117 -0
  33. package/src/presentation/line-fitting.ts +27 -0
  34. package/src/presentation/path-ask-payload.ts +128 -0
  35. package/src/presentation/prompt-payload.ts +137 -0
  36. package/src/presentation/skill-ask-payload.ts +50 -0
  37. package/src/presentation/tool-ask-payload.ts +104 -0
  38. package/src/tool-preview-formatter.ts +1 -1
  39. package/src/types.ts +6 -0
  40. 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
- Pi's tool-expansion binding (`app.tools.expand`, `Ctrl+O` by default) keeps working while the dialog is open, so you can expand a truncated tool preview before deciding.
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.
@@ -8,6 +8,9 @@
8
8
 
9
9
  "forwardingTimeoutMs": 600000,
10
10
 
11
+ "promptMaxRows": 24,
12
+ "promptFieldMaxWidth": 400,
13
+
11
14
  "toolInputPreviewMaxLength": 400,
12
15
  "toolTextSummaryMaxLength": 120,
13
16
 
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;
@@ -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, so you can expand a truncated tool preview before deciding.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-permission-system",
3
- "version": "25.2.1",
3
+ "version": "25.3.0",
4
4
  "description": "Permission enforcement extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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.",