@gotgenes/pi-permission-system 9.2.0 → 10.1.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 +52 -0
- package/README.md +12 -11
- package/package.json +1 -1
- package/src/agent-prep-session.ts +28 -0
- package/src/decision-reporter.ts +41 -0
- package/src/denial-messages.ts +11 -0
- package/src/forwarded-permissions/io.ts +29 -0
- package/src/forwarded-permissions/permission-forwarder.ts +549 -0
- package/src/forwarding-manager.ts +3 -7
- package/src/gate-handler-session.ts +13 -0
- package/src/gate-prompter.ts +14 -0
- package/src/handlers/before-agent-start.ts +2 -3
- package/src/handlers/gates/bash-command.ts +4 -18
- package/src/handlers/gates/bash-external-directory.ts +3 -15
- package/src/handlers/gates/bash-path.ts +3 -16
- package/src/handlers/gates/descriptor.ts +0 -28
- package/src/handlers/gates/path.ts +3 -15
- package/src/handlers/gates/runner.ts +142 -105
- package/src/handlers/gates/skill-input-gate-pipeline.ts +104 -0
- package/src/handlers/gates/skill-input.ts +44 -0
- package/src/handlers/gates/tool-call-gate-pipeline.ts +120 -0
- package/src/handlers/lifecycle.ts +9 -9
- package/src/handlers/permission-gate-handler.ts +34 -238
- package/src/index.ts +50 -68
- package/src/mcp-targets.ts +56 -46
- package/src/permission-event-rpc.ts +7 -0
- package/src/permission-events.ts +89 -8
- package/src/permission-forwarding.ts +23 -0
- package/src/permission-prompter.ts +27 -56
- package/src/permission-resolver.ts +17 -0
- package/src/permission-session.ts +77 -9
- package/src/permission-ui-prompt.ts +127 -0
- package/src/permissions-service.ts +53 -0
- package/src/service-lifecycle.ts +49 -0
- package/src/service.ts +17 -0
- package/src/session-approval-recorder.ts +6 -0
- package/src/session-lifecycle-session.ts +24 -0
- package/src/tool-input-preview.ts +0 -62
- package/src/tool-input-prompt-formatters.ts +63 -0
- package/src/tool-preview-formatter.ts +6 -4
- package/test/composition-root.test.ts +5 -0
- package/test/decision-reporter.test.ts +112 -0
- package/test/denial-messages.test.ts +62 -0
- package/test/forwarding-manager.test.ts +26 -44
- package/test/handlers/before-agent-start.test.ts +45 -21
- package/test/handlers/external-directory-integration.test.ts +86 -22
- package/test/handlers/external-directory-session-dedup.test.ts +102 -55
- package/test/handlers/gates/bash-command.test.ts +49 -90
- package/test/handlers/gates/bash-external-directory.test.ts +54 -95
- package/test/handlers/gates/bash-path.test.ts +63 -148
- package/test/handlers/gates/path.test.ts +38 -105
- package/test/handlers/gates/runner.test.ts +150 -93
- package/test/handlers/gates/skill-input-gate-pipeline.test.ts +176 -0
- package/test/handlers/gates/skill-input.test.ts +128 -0
- package/test/handlers/gates/tool-call-gate-pipeline.test.ts +180 -0
- package/test/handlers/input.test.ts +1 -2
- package/test/handlers/lifecycle.test.ts +49 -33
- package/test/handlers/tool-call-events.test.ts +1 -1
- package/test/helpers/gate-fixtures.ts +147 -16
- package/test/helpers/handler-fixtures.ts +143 -27
- package/test/mcp-targets.test.ts +55 -0
- package/test/permission-event-rpc.test.ts +39 -0
- package/test/permission-events.test.ts +78 -10
- package/test/permission-forwarder.test.ts +295 -0
- package/test/permission-prompter.test.ts +147 -38
- package/test/permission-session.test.ts +160 -27
- package/test/permission-ui-prompt.test.ts +146 -0
- package/test/permissions-service.test.ts +151 -0
- package/test/runtime.test.ts +0 -4
- package/test/service-lifecycle.test.ts +162 -0
- package/test/tool-input-preview.test.ts +0 -111
- package/test/tool-input-prompt-formatters.test.ts +115 -0
- package/src/forwarded-permissions/polling.ts +0 -379
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,58 @@ 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
|
+
## [10.1.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v10.0.0...pi-permission-system-v10.1.0) (2026-06-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add DecisionReporter and GateDecisionReporter ([530211d](https://github.com/gotgenes/pi-packages/commit/530211da9158a012e86f37311e326f4e2b571c55))
|
|
14
|
+
* add GatePrompter and SessionApprovalRecorder session roles ([2f761e4](https://github.com/gotgenes/pi-packages/commit/2f761e44fd07c98fe98147275f75fc170163b06d))
|
|
15
|
+
* add GateRunner class consolidating gate dispatch ([a390558](https://github.com/gotgenes/pi-packages/commit/a390558f19723fa911924b2d1878d4d874d6966d))
|
|
16
|
+
* add getToolPreviewLimits and getInfrastructureReadDirs to PermissionSession ([#327](https://github.com/gotgenes/pi-packages/issues/327)) ([a0bf166](https://github.com/gotgenes/pi-packages/commit/a0bf1662119eeeb4b390c668c6993c7ff87194bf))
|
|
17
|
+
* add PermissionResolver.resolve to PermissionSession ([c922bbd](https://github.com/gotgenes/pi-packages/commit/c922bbddcfb47a2ec88d349f3a797b633bd58f45))
|
|
18
|
+
* add skill_input denial context ([#326](https://github.com/gotgenes/pi-packages/issues/326)) ([71e9d28](https://github.com/gotgenes/pi-packages/commit/71e9d28c5f6c8a09d2bfa9fe21cca6c55948898b))
|
|
19
|
+
* introduce SkillInputGatePipeline collaborator ([#329](https://github.com/gotgenes/pi-packages/issues/329)) ([4ddd5af](https://github.com/gotgenes/pi-packages/commit/4ddd5af1c476ab3c3eb29ce456a33b273c386ca0))
|
|
20
|
+
* introduce ToolCallGatePipeline collaborator ([#327](https://github.com/gotgenes/pi-packages/issues/327)) ([3a87727](https://github.com/gotgenes/pi-packages/commit/3a877274091bfc2db3998ca915f76ecbdd2ac1e7))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* drop vestigial events field; document makeReporter in package skill ([9e0a8a7](https://github.com/gotgenes/pi-packages/commit/9e0a8a7d89b4c081d21465e02b7aa77c18ddd1b0))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
* document SkillInputGatePipeline in architecture and package skill ([#329](https://github.com/gotgenes/pi-packages/issues/329)) ([9193c86](https://github.com/gotgenes/pi-packages/commit/9193c86ecc2fa6b18da6a7c7e4a9f9efbbc807ed))
|
|
31
|
+
* record the composition-root collaborator extraction ([#320](https://github.com/gotgenes/pi-packages/issues/320)) ([dab8890](https://github.com/gotgenes/pi-packages/commit/dab8890df05e003bb9136924ab2d344c7fe69319))
|
|
32
|
+
* standardize and correct package READMEs ([4c270ad](https://github.com/gotgenes/pi-packages/commit/4c270adac97ca816fa1889a879d1d4fe19cdd464))
|
|
33
|
+
|
|
34
|
+
## [10.0.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v9.2.0...pi-permission-system-v10.0.0) (2026-06-02)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### ⚠ BREAKING CHANGES
|
|
38
|
+
|
|
39
|
+
* **pi-permission-system:** the permissions:ready event payload no longer includes protocolVersion. Consumers that read it must rely on package semver instead.
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
|
|
43
|
+
* **pi-permission-manager:** broadcast permission prompts on permissions:prompt channel ([8540f3b](https://github.com/gotgenes/pi-packages/commit/8540f3b462b76a4789c4c17a75fadf254ae39feb))
|
|
44
|
+
* **pi-permission-system:** drop protocolVersion from permissions:ready ([6728a93](https://github.com/gotgenes/pi-packages/commit/6728a93af7edbc6953d20f448f1c3f54f9b7893f))
|
|
45
|
+
* **pi-permission-system:** harden prompt broadcasts ([067bafd](https://github.com/gotgenes/pi-packages/commit/067bafd80ef983fd8b9ab00914cf1cec9b6db915))
|
|
46
|
+
* **pi-permission-system:** make ready and decision broadcasts best-effort ([00a895f](https://github.com/gotgenes/pi-packages/commit/00a895f9377bcb7b598acbc6c95d7bc7cc83c515))
|
|
47
|
+
* **pi-permission-system:** preserve display fields for forwarded prompts ([9970912](https://github.com/gotgenes/pi-packages/commit/997091228736bbd4395d8bd16aeb9f4a4ae7e0b2))
|
|
48
|
+
* **pi-permission-system:** slim ui_prompt payload and centralize construction ([7a1ec56](https://github.com/gotgenes/pi-packages/commit/7a1ec56a827e90fe80a0f7de48e1222b5271700d))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Bug Fixes
|
|
52
|
+
|
|
53
|
+
* **pi-permission-system:** drop manual CHANGELOG Unreleased section ([f14e4f5](https://github.com/gotgenes/pi-packages/commit/f14e4f5d9b5ae1d6b207a913aefdd71980a46dd6))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Documentation
|
|
57
|
+
|
|
58
|
+
* **pi-permission-system:** document the lean ui_prompt contract ([0b3c11c](https://github.com/gotgenes/pi-packages/commit/0b3c11c57a7b718d2f73a184802f8c5dcb95fbe7))
|
|
59
|
+
|
|
8
60
|
## [9.2.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v9.1.0...pi-permission-system-v9.2.0) (2026-06-02)
|
|
9
61
|
|
|
10
62
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# @gotgenes/pi-permission-system
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/@gotgenes/pi-permission-system) [](https://www.npmjs.com/package/@gotgenes/pi-permission-system) [](https://github.com/gotgenes/pi-packages/actions/workflows/ci.yml) [](https://opensource.org/licenses/MIT) [](https://www.typescriptlang.org/) [](https://pnpm.io/) [](https://pi.mariozechner.at/)
|
|
8
8
|
|
|
9
9
|
Permission enforcement extension for the [Pi](https://pi.mariozechner.at/) coding agent that provides centralized, deterministic permission gates over tool, bash, MCP, skill, and special operations.
|
|
10
10
|
|
|
@@ -20,6 +20,7 @@ Permission enforcement extension for the [Pi](https://pi.mariozechner.at/) codin
|
|
|
20
20
|
- **Protects sensitive file patterns** — cross-cutting `path` rules deny `.env`, `~/.ssh/*`, etc. across all tools and bash at once
|
|
21
21
|
- **Guards external paths** — prompts before file tools or bash commands reach outside `cwd`
|
|
22
22
|
- **Forwards prompts from subagents** — `ask` policies work even in non-UI execution contexts
|
|
23
|
+
- **Broadcasts UI prompt events** — `permissions:ui_prompt` fires only when the permission system is about to invoke the active user-facing permission UI
|
|
23
24
|
- **Native [`@gotgenes/pi-subagents`](https://github.com/gotgenes/pi-subagents) integration** — in-process child sessions register with the permission system automatically, enabling per-agent policy enforcement and `ask`-state forwarding to the parent UI without configuration
|
|
24
25
|
|
|
25
26
|
## Install
|
|
@@ -89,16 +90,16 @@ For the full reference — all surfaces, runtime knobs, per-agent overrides, mer
|
|
|
89
90
|
|
|
90
91
|
## Documentation
|
|
91
92
|
|
|
92
|
-
| Document | Contents
|
|
93
|
-
| ------------------------------------------------------------------------------------------------------------------------------ |
|
|
94
|
-
| [docs/configuration.md](docs/configuration.md) | Full policy reference, runtime knobs, per-agent overrides, recipes
|
|
95
|
-
| [docs/session-approvals.md](docs/session-approvals.md) | Session-scoped rules, pattern suggestions, bash arity table
|
|
96
|
-
| [docs/cross-extension-api.md](docs/cross-extension-api.md) | Cross-extension service accessor, event bus integration, decision broadcasts |
|
|
97
|
-
| [docs/subagent-integration.md](docs/subagent-integration.md) | Permission forwarding, coexistence with subagent extensions
|
|
98
|
-
| [docs/guides/permission-frontmatter-for-subagent-extensions.md](docs/guides/permission-frontmatter-for-subagent-extensions.md) | Convention guide for subagent extension authors
|
|
99
|
-
| [docs/opencode-compatibility.md](docs/opencode-compatibility.md) | OpenCode compatibility — shared concepts, divergences, porting guide
|
|
100
|
-
| [docs/troubleshooting.md](docs/troubleshooting.md) | Common issues, diagnostic logging, threat model
|
|
101
|
-
| [docs/migration/legacy-to-flat.md](docs/migration/legacy-to-flat.md) | Migration from pre-v2 config layout
|
|
93
|
+
| Document | Contents |
|
|
94
|
+
| ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
|
95
|
+
| [docs/configuration.md](docs/configuration.md) | Full policy reference, runtime knobs, per-agent overrides, recipes |
|
|
96
|
+
| [docs/session-approvals.md](docs/session-approvals.md) | Session-scoped rules, pattern suggestions, bash arity table |
|
|
97
|
+
| [docs/cross-extension-api.md](docs/cross-extension-api.md) | Cross-extension service accessor, event bus integration, prompt and decision broadcasts |
|
|
98
|
+
| [docs/subagent-integration.md](docs/subagent-integration.md) | Permission forwarding, coexistence with subagent extensions |
|
|
99
|
+
| [docs/guides/permission-frontmatter-for-subagent-extensions.md](docs/guides/permission-frontmatter-for-subagent-extensions.md) | Convention guide for subagent extension authors |
|
|
100
|
+
| [docs/opencode-compatibility.md](docs/opencode-compatibility.md) | OpenCode compatibility — shared concepts, divergences, porting guide |
|
|
101
|
+
| [docs/troubleshooting.md](docs/troubleshooting.md) | Common issues, diagnostic logging, threat model |
|
|
102
|
+
| [docs/migration/legacy-to-flat.md](docs/migration/legacy-to-flat.md) | Migration from pre-v2 config layout |
|
|
102
103
|
|
|
103
104
|
## Development
|
|
104
105
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
import type { GateHandlerSession } from "./gate-handler-session";
|
|
4
|
+
import type {
|
|
5
|
+
SkillPermissionChecker,
|
|
6
|
+
SkillPromptEntry,
|
|
7
|
+
} from "./skill-prompt-sanitizer";
|
|
8
|
+
import type { PermissionState } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The session surface `AgentPrepHandler` invokes during `before_agent_start`:
|
|
12
|
+
* bind context + identify the agent (via {@link GateHandlerSession}), check
|
|
13
|
+
* skill permissions for prompt sanitization (via {@link SkillPermissionChecker}),
|
|
14
|
+
* refresh config, decide tool exposure, manage the active-tools / prompt-state
|
|
15
|
+
* cache keys, and store the resolved skill entries.
|
|
16
|
+
*/
|
|
17
|
+
export interface AgentPrepSession
|
|
18
|
+
extends GateHandlerSession,
|
|
19
|
+
SkillPermissionChecker {
|
|
20
|
+
refreshConfig(ctx?: ExtensionContext): void;
|
|
21
|
+
getToolPermission(toolName: string, agentName?: string): PermissionState;
|
|
22
|
+
shouldUpdateActiveTools(cacheKey: string): boolean;
|
|
23
|
+
commitActiveToolsCacheKey(cacheKey: string): void;
|
|
24
|
+
getPolicyCacheStamp(agentName?: string): string;
|
|
25
|
+
shouldUpdatePromptState(cacheKey: string): boolean;
|
|
26
|
+
commitPromptStateCacheKey(cacheKey: string): void;
|
|
27
|
+
setActiveSkillEntries(entries: SkillPromptEntry[]): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
emitDecisionEvent,
|
|
3
|
+
type PermissionDecisionEvent,
|
|
4
|
+
type PermissionEventBus,
|
|
5
|
+
} from "./permission-events";
|
|
6
|
+
import type { SessionLogger } from "./session-logger";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reports a permission gate's outcome to the review log and the decision
|
|
10
|
+
* channel. Groups the two side effects that always travel together:
|
|
11
|
+
* writing a structured review-log entry and broadcasting a decision event.
|
|
12
|
+
*/
|
|
13
|
+
export interface DecisionReporter {
|
|
14
|
+
writeReviewLog(event: string, details: Record<string, unknown>): void;
|
|
15
|
+
emitDecision(event: PermissionDecisionEvent): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Owns the `SessionLogger` and the event bus so neither the handler nor
|
|
20
|
+
* the runner has to reach through the session to its logger or close over
|
|
21
|
+
* the event bus directly.
|
|
22
|
+
*
|
|
23
|
+
* Built once in `PermissionGateHandler`'s constructor; shared between
|
|
24
|
+
* `handleToolCall` (gate runner + bypass branch) and `handleInput`.
|
|
25
|
+
*
|
|
26
|
+
* Answers "who owns the event bus" — the reporter does, not the session.
|
|
27
|
+
*/
|
|
28
|
+
export class GateDecisionReporter implements DecisionReporter {
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly logger: SessionLogger,
|
|
31
|
+
private readonly events: PermissionEventBus,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
writeReviewLog(event: string, details: Record<string, unknown>): void {
|
|
35
|
+
this.logger.review(event, details);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
emitDecision(event: PermissionDecisionEvent): void {
|
|
39
|
+
emitDecisionEvent(this.events, event);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/denial-messages.ts
CHANGED
|
@@ -45,6 +45,11 @@ export type DenialContext =
|
|
|
45
45
|
skillName: string;
|
|
46
46
|
readPath: string;
|
|
47
47
|
agentName?: string;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
kind: "skill_input";
|
|
51
|
+
skillName: string;
|
|
52
|
+
agentName?: string;
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
// ── Public formatter API ───────────────────────────────────────────────────
|
|
@@ -91,6 +96,8 @@ function buildDenyBody(ctx: DenialContext): string {
|
|
|
91
96
|
return `${subject(ctx.agentName)} is not permitted to access path '${ctx.pathValue}' via tool 'bash'.`;
|
|
92
97
|
case "skill_read":
|
|
93
98
|
return `${subject(ctx.agentName)} is not permitted to access skill '${ctx.skillName}' via '${ctx.readPath}'.`;
|
|
99
|
+
case "skill_input":
|
|
100
|
+
return `${subject(ctx.agentName)} is not permitted to access skill '${ctx.skillName}'.`;
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
|
|
@@ -184,6 +191,8 @@ function buildUnavailableBody(ctx: DenialContext): string {
|
|
|
184
191
|
return `Bash command '${ctx.command}' accesses path '${ctx.pathValue}' which requires approval, but no interactive UI is available.`;
|
|
185
192
|
case "skill_read":
|
|
186
193
|
return `Accessing skill '${ctx.skillName}' requires approval, but no interactive UI is available.`;
|
|
194
|
+
case "skill_input":
|
|
195
|
+
return `Accessing skill '${ctx.skillName}' requires approval, but no interactive UI is available.`;
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
198
|
|
|
@@ -212,6 +221,8 @@ function buildUserDeniedBody(
|
|
|
212
221
|
return `User denied path access for bash command '${ctx.command}' (path '${ctx.pathValue}').${reasonSuffix(denialReason)}`;
|
|
213
222
|
case "skill_read":
|
|
214
223
|
return `User denied access to skill '${ctx.skillName}'.${reasonSuffix(denialReason)}`;
|
|
224
|
+
case "skill_input":
|
|
225
|
+
return `User denied access to skill '${ctx.skillName}'.${reasonSuffix(denialReason)}`;
|
|
215
226
|
}
|
|
216
227
|
}
|
|
217
228
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
|
|
12
12
|
import { isPermissionDecisionState } from "#src/permission-dialog";
|
|
13
|
+
import type { PermissionUiPromptSource } from "#src/permission-events";
|
|
13
14
|
import {
|
|
14
15
|
createPermissionForwardingLocation,
|
|
15
16
|
type ForwardedPermissionRequest,
|
|
@@ -17,6 +18,29 @@ import {
|
|
|
17
18
|
type PermissionForwardingLocation,
|
|
18
19
|
} from "#src/permission-forwarding";
|
|
19
20
|
|
|
21
|
+
/** Valid `permissions:ui_prompt` source values, for tolerant request reads. */
|
|
22
|
+
const UI_PROMPT_SOURCES = [
|
|
23
|
+
"tool_call",
|
|
24
|
+
"skill_input",
|
|
25
|
+
"skill_read",
|
|
26
|
+
"rpc_prompt",
|
|
27
|
+
] as const satisfies readonly PermissionUiPromptSource[];
|
|
28
|
+
|
|
29
|
+
/** Narrow an unknown value to a valid prompt source, or `undefined`. */
|
|
30
|
+
function asUiPromptSource(
|
|
31
|
+
value: unknown,
|
|
32
|
+
): PermissionUiPromptSource | undefined {
|
|
33
|
+
return UI_PROMPT_SOURCES.find((source) => source === value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Narrow an unknown value to a nullable display string, or `undefined`. */
|
|
37
|
+
function asNullableDisplayString(value: unknown): string | null | undefined {
|
|
38
|
+
if (value === null || typeof value === "string") {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
20
44
|
type LogFn = (event: string, details: Record<string, unknown>) => void;
|
|
21
45
|
|
|
22
46
|
export interface ForwardedPermissionLogger {
|
|
@@ -285,6 +309,11 @@ export function readForwardedPermissionRequest(
|
|
|
285
309
|
targetSessionId: parsed.targetSessionId,
|
|
286
310
|
requesterAgentName: parsed.requesterAgentName,
|
|
287
311
|
message: parsed.message,
|
|
312
|
+
// Tolerant read: display fields are optional and may be absent (older
|
|
313
|
+
// child) or malformed; reconstruct only the well-formed ones.
|
|
314
|
+
source: asUiPromptSource(parsed.source),
|
|
315
|
+
surface: asNullableDisplayString(parsed.surface),
|
|
316
|
+
value: asNullableDisplayString(parsed.value),
|
|
288
317
|
};
|
|
289
318
|
} catch (error) {
|
|
290
319
|
logPermissionForwardingWarning(
|